在看看Rust的时候,我发现了一个我不太明白的行为。
我已经获得了此代码,该代码按预期工作:
fn get_or_create_foo(v: &mut Vec<String>) -> String {
match v.get(0) {
Some(x) => return x.clone(),
None => ()
}
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap().clone()
}
fn main() {
let mut v = Vec::new();
println!("{}", get_or_create_foo(&mut v));
println!("{}", get_or_create_foo(&mut v));
}
当我更改get_or_create_foo()
以使其返回借用的字符串切片时,编译器拒绝编译它。
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
match v.get(0) {
Some(x) => return x,
None => ()
}
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap()
}
编译日志:
$ rustc --verbose src/main.rs
src/main.rs:8:5: 8:6 error: cannot borrow `*v` as mutable because it is also borrowed as immutable
src/main.rs:8 v.push("foo".to_string());
^
src/main.rs:2:11: 2:12 note: previous borrow of `*v` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `*v` until the borrow ends
src/main.rs:2 match v.get(0) {
^
src/main.rs:10:2: 10:2 note: previous borrow ends here
src/main.rs:1 fn get_or_create_foo(v: &mut Vec<String>) -> &str {
...
src/main.rs:10 }
^
error: aborting due to previous error
在我的理解中,代码是有效的:一旦控制离开match
子句,通过采用导致代码变异v
的路径,就可以返回所提到的借用。
我错了吗?允许这样的代码会导致问题吗?
答案 0 :(得分:2)
我不确切知道,但我怀疑你的代码:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
match v.get(0) {
Some(x) => return x,
None => ()
}
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap()
}
通过消除显式return
,编译器将翻译成具有等效语法的东西,如下所示:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
match v.get(0) {
Some(x) => x,
None => {
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap()
},
}
}
显然失败并出现同样的错误。此处get
生成Option<&String>
,因此v
即使在None
分支中仍然借用,其中不会捕获任何引用。
幸运的是,有一种简单的方法可以重写该功能:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
if v.get(0).is_none() {
println!("creating foo");
v.push("foo".to_string());
}
v.get(0).unwrap()
}
答案 1 :(得分:2)
您可以略微改进swizard的解决方案:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
if v.is_empty() {
println!("creating foo");
v.push("foo".to_string());
}
&v[0]
}
答案 2 :(得分:1)
我也是Rust的新手,但我相信我可能找到了问题的根源。
您可以检查&#34; get&#34;的类型签名。函数here。如你所见,&#34;得到&#34; function返回对所请求的向量成员的借用引用(包含在Option中)。我的猜测是编译器无法在你的情况下验证&#34; x&#34;不能逃避&#34;来自匹配区。
以下是来自 A 30-minute Introduction to Rust 的更简单但类似的示例:
fn main() { let mut v = vec![]; v.push("Hello"); let x = &v[0]; v.push("world"); println!("{}", x); }
在Rust中,类型系统编码所有权的概念。变量v是向量的所有者。当我们引用v时,我们让那个变量(在这种情况下,x)借用它一段时间。就像你拥有一本书,然后借给我,我就借这本书。
所以,当我尝试使用push的第二次调用来修改向量时,我需要拥有它。但是x正在借用它。您无法修改您借给某人的内容。因此Rust抛出错误。
以下是我对其进行成像的方法:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
let a: &str;
match v.get(0) {
Some(x) => {
a = x;
return x;
},
None => ()
}
// Now "a" is still borrowing "v" immutably!
// println!("{:?}", a);
println!("creating foo");
v.push("foo".to_string());
v.get(0).unwrap()
}
正如我所说,我还是初学者,所以可能还有更多。在对你的代码进行了一些调整之后,我得出了我的结论。
一个简单的重构可以解决这个问题:
fn get_or_create_foo(v: &mut Vec<String>) -> &str {
match v.get(0) {
// Notice how the borrowed value is never used and
// thus can not "escape" our match block.
Some(_) => (),
_ => v.push("foo".to_string())
}
// No need to use "get" here since we are 100% sure that
// the indexed vector contains at least one item.
return &v[0];
}