在以下代码(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
}
Run Code Online (Sandbox Code Playgroud)
我有这个错误:
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
Run Code Online (Sandbox Code Playgroud)
我的问题似乎非常类似于 为什么Rust想要一次多次使用变量作为可变变量?,但我不确定.如果是这样,这种情况有解决方法吗?
Vla*_*eev 17
这add
是因为如何定义:
fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node
Run Code Online (Sandbox Code Playgroud)
这里指定了结果引用的生命周期应该等于传入引用的生命周期.可能的唯一方法(除了不安全的代码)是结果引用以某种方式从传入引用派生,例如,它引用传入引用指向的对象内的一些字段:
struct X {
a: u32,
b: u32,
}
fn borrow_a<'a>(x: &'a mut X) -> &'a mut u32 {
&mut x.a
}
Run Code Online (Sandbox Code Playgroud)
然而,有没有办法让编译器知道到底是什么从进入结构由只在函数签名(其中,在一般情况下,是在编译使用这种功能的代码时,它可以做的唯一的事情)看借来的.因此,它无法知道以下代码在技术上是正确的:
let mut x = X { a: 1, b: 2 };
let a = borrow_a(&mut x);
let b = &mut x.b;
Run Code Online (Sandbox Code Playgroud)
我们知道a
并且b
是不相交的,因为它们指向结构的不同部分,但是编译器无法知道,因为borrow_a
签名中没有任何东西可以暗示它(并且不存在,Rust不支持它) .
因此,编译器可以做的唯一明智的事情是考虑整个 x
被借用,直到返回的引用borrow_a()
被删除.否则,可以为相同的数据创建两个可变引用,这违反了Rust别名保证.
请注意以下代码是正确的:
let mut x = X { a: 1, b: 2 };
let a = &mut x.a;
let b = &mut x.b;
Run Code Online (Sandbox Code Playgroud)
在这里,编译器可以看到a
并且b
从不指向相同的数据,即使它们确实指向同一结构内部.
没有解决方法,唯一的解决方案是重构代码,使其没有这种借用模式.
该行为是合乎逻辑的。考虑什么
fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node
Run Code Online (Sandbox Code Playgroud)
方法。
这表示它&mut Node
的生命周期等于其返回值的生命周期。因为您将返回值分配给一个名称,所以它会一直存在到作用域末尾。因此,可变借用的寿命也很长。
如果您可以轻松丢弃返回值,请这样做。你可以把它扔在地板上:
let mut tree = Node {
datum: "start",
edges: Vec::new(),
};
add(&mut tree, "level1");
traverse(&mut tree, &|x| println!("{:}", x));
Run Code Online (Sandbox Code Playgroud)
或者您可以使用词法范围来约束它而不完全删除它。
如果你想借用回报而不强迫可变借用也活那么长时间,你可能必须将函数分成两部分。这是因为您无法借用可变借用的返回值来执行此操作。