通过迭代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
这是有道理的,因为anchor
和node
指向相同的结构,但是在解构它之后,我实际上并不在意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 }