不能一次多次使用`x`作为可变的

时间:2015-07-07 23:32:54

标签: rust

在以下代码(playground)中:

struct Node {
    datum: &'static str,
    edges: Vec<Node>,
}

fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node {
    node.edges.push(Node {
        datum: data,
        edges: Vec::new(),
    });
    &node.edges[node.edges.len() - 1] // return just added one
}

fn traverse<F>(root: &Node, callback: &F)
where
    F: Fn(&'static str),
{
    callback(root.datum);
    for node in &root.edges {
        traverse(node, callback);
    }
}

fn main() {
    let mut tree = Node {
        datum: "start",
        edges: Vec::new(),
    };

    let lvl1 = add(&mut tree, "level1");

    traverse(&mut tree, &|x| println!("{:}", x)); //I actually don't need mutability here
}

我有这个错误:

error[E0499]: cannot borrow `tree` as mutable more than once at a time
  --> src/main.rs:32:19
   |
30 |     let lvl1 = add(&mut tree, "level1");
   |                         ---- first mutable borrow occurs here
31 | 
32 |     traverse(&mut tree, &|x| println!("{:}", x)); //I actually don't need mutability here
   |                   ^^^^ second mutable borrow occurs here
33 | }
   | - first borrow ends here

我的问题似乎与Why does Rust want to borrow a variable as mutable more than once at a time?非常相似,但我不确定。如果是这样,这种情况是否有解决方法?

2 个答案:

答案 0 :(得分:13)

这是因为定义了add的原因:

fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node

这里指定了结果引用的生命周期应该等于传入引用的生命周期。 唯一方式是可能的(除了不安全的代码)是结果引用以某种方式从传入引用派生,例如,它引用传入引用指向的对象内的一些字段:

struct X {
    a: u32,
    b: u32,
}

fn borrow_a<'a>(x: &'a mut X) -> &'a mut u32 {
    &mut x.a
}

然而,编译器有 no 的方式通过仅查看函数签名来知道从传入结构中确切地是什么(通常,它是编译使用此函数的代码时唯一可以做的事情。因此,它无法知道以下代码在技术上是正确的:

let mut x = X { a: 1, b: 2 };
let a = borrow_a(&mut x);
let b = &mut x.b;

我们知道ab是不相交的,因为它们指向结构的不同部分,但编译器无法知道,因为{ {1}}的签名表明它(并且不可能,Rust不支持它)。

因此,编译器可以做的唯一明智的事情是考虑整个 borrow_a被借用,直到x返回的引用被删除。否则,可能会为相同的数据创建两个可变引用,这违反了Rust别名保证。

请注意以下代码是正确的:

borrow_a()

这里编译器可以看到let mut x = X { a: 1, b: 2 }; let a = &mut x.a; let b = &mut x.b; a从不指向相同的数据,即使它们指向同一个结构内部。

没有解决方法,唯一的解决方案是重构代码,使其没有这种借用模式。

答案 1 :(得分:3)

行为是合乎逻辑的。考虑一下

fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node

的装置。

这表示&mut Node的生命周期等于到其返回值的生命周期。因为您将返回值分配给名称,所以它将一直存在到作用域的末尾。因此,可变借款也存在很长时间。

如果您可以轻松丢弃返回值,请执行此操作。你可以把它放在地板上:

let mut tree = Node {
    datum: "start",
    edges: Vec::new(),
};

add(&mut tree, "level1");

traverse(&mut tree, &|x| println!("{:}", x));

或者您可以使用词法范围来约束它而不会完全丢弃它。

如果你想借用返回而不用强制可变借用也存活那么久,你可能不得不将该功能分成两部分。这是因为您无法从可变借入中借用返回值来执行此操作。