匹配语句,显式返回借用的引用

时间:2015-03-06 19:01:41

标签: rust borrow-checker

在看看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的路径,就可以返回所提到的借用。

我错了吗?允许这样的代码会导致问题吗?

3 个答案:

答案 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];
}