我试图迭代地导航递归数据结构,以便在某个位置插入元素 . 根据我的有限理解,这意味着对结构的根进行可变引用,并通过对其跟随者的引用连续替换它:
type Link = Option<Box<Node>>;
struct Node {
next: Link
}
struct Recursive {
root: Link
}
impl Recursive {
fn back(&mut self) -> &mut Link {
let mut anchor = &mut self.root;
while let Some(ref mut node) = *anchor {
anchor = &mut node.next;
}
anchor
}
}
但是,这失败了:
error[E0499]: cannot borrow `anchor.0` as mutable more than once at a time
--> src/main.rs:14:24
|
14 | while let Some(ref mut node) = *anchor {
| ^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first mutable borrow occurs here
...
18 | }
| - first borrow ends here
error[E0506]: cannot assign to `anchor` because it is borrowed
--> src/main.rs:15:13
|
14 | while let Some(ref mut node) = *anchor {
| ------------ borrow of `anchor` occurs here
15 | anchor = &mut node.next;
| ^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `anchor` occurs here
error[E0499]: cannot borrow `*anchor` as mutable more than once at a time
--> src/main.rs:17:9
|
14 | while let Some(ref mut node) = *anchor {
| ------------ first mutable borrow occurs here
...
17 | anchor
| ^^^^^^ second mutable borrow occurs here
18 | }
| - first borrow ends here
这是有意义的,因为 anchor
和 node
都指的是相同的结构,但我实际上在解构它之后不再关心 anchor
.
如何使用安全的Rust正确实现 back()
?
4 回答
有可能......但我希望我有一个更优雅的解决方案 .
诀窍是不要从
anchor
借钱,因此要在两个累加器之间徘徊:一个持有对当前节点的引用
另一个被分配了对下一个节点的引用
这导致我:
不完全漂亮,但这是借用检查员可以落后的东西所以¯\ (ツ) /¯ .
@ker通过创建一个未命名的临时文件来改进:
这里的技巧是使用
{anchor}
将anchor
的内容移动到匹配执行的未命名临时文件中 . 因此,在match
块中我们不是从anchor
借来的,而是从临时借来的,让我们可以自由修改anchor
. 请参阅相关博客文章Stuff the Identity Function Does (in Rust) .您可以使用递归来满足借用检查器 . 这样做的缺点是为列表中的每个项目创建堆栈框架 . 如果你的列表很长,这肯定会遇到堆栈溢出 . LLVM会将
Node::back
方法优化为循环(请参阅playground上生成的LLVM IR)原始代码按原样运行non-lexical lifetimes:
非词法生命周期提高了编译器借用检查器的精度,允许它看到不再使用
anchor
的可变借位 . 由于最近的语言更改,我们还可以简化if let
中的关键字 .有用: