通过迭代recursion结构来获得可变引用

我试图迭代地浏览一个recursion数据结构,以便在某个位置插入元素。 就我的有限理解而言,这意味着要对结构的根源进行可变的引用,并且通过引用其追随者来替代它:

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 

这是有道理的,因为anchornode指向相同的结构,但是在解构它之后,我实际上并不在意anchor

如何使用安全的Rust可以正确实现back()

这是可能的…但我希望我有一个更优雅的解决scheme。

诀窍是不要从anchor借用,因此要在两个累加器之间进行操作:

  • 一个持有对当前节点的引用
  • 另一个被分配给下一个节点的引用

这导致我:

 impl Recursive { fn back(&mut self) -> &mut Link { let mut anchor = &mut self.root; loop { let tmp = anchor; if let Some(ref mut node) = *tmp { anchor = &mut node.next; } else { anchor = tmp; break; } } anchor } } 

不完全漂亮,但这是借用检查器可以得到的东西,所以¯\ _(ツ)_ /¯。

@ker通过创build一个未命名的临时文件改进了这一点:

 impl Recursive { fn back(&mut self) -> &mut Link { let mut anchor = &mut self.root; loop { match {anchor} { &mut Some(ref mut node) => anchor = &mut node.next, other => return other, } } } } 

这里的技巧是使用{anchor} {anchor}的内容移动到执行匹配的未命名的临时文件中。 因此,在match我们不是从anchor而是从暂时借用,让我们自由地修改anchor 。 查看相关的博客文章东西的身份function是否(在锈) 。

您可以使用recursion来满足借用检查器。 这有一个缺点,就是为列表中的每个项目创build一个堆栈框架。 如果你的列表很长,这肯定会遇到堆栈溢出。 LLVM会将Node::back方法优化为一个循环(请参阅在操场上生成的LLVM IR)

 impl Node { fn back(&mut self) -> &mut Link { match self.next { Some(ref mut node) => node.back(), None => &mut self.next, } } } impl Recursive { fn back(&mut self) -> Option<&mut Link> { self.root.as_mut().map(|node| node.back()) } } 

有用:

 fn back(&mut self) -> &mut Link { let mut anchor = &mut self.root; while anchor.is_some(){ anchor = &mut {anchor}.as_mut().unwrap().next; } anchor } 
Interesting Posts