在以下代码(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?非常相似,但我不确定。如果是这样,这种情况是否有解决方法?
答案 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;
我们知道a
和b
是不相交的,因为它们指向结构的不同部分,但编译器无法知道,因为{ {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));
或者您可以使用词法范围来约束它而不会完全丢弃它。
如果你想借用返回而不用强制可变借用也存活那么久,你可能不得不将该功能分成两部分。这是因为您无法从可变借入中借用返回值来执行此操作。