迭代递归结构时无法获得可变引用:一次不能多次借用可变引用 [英] Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time

查看:24
本文介绍了迭代递归结构时无法获得可变引用:一次不能多次借用可变引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试以迭代方式导航递归数据结构,以便在特定位置插入元素.就我有限的理解而言,这意味着对结构的根进行可变引用,并连续将其替换为对其跟随者的引用:

type Link = Option>;结构节点{下一个: 链接}结构递归{根:链接}实现递归{fn back(&mut self) ->&mut 链接{让 mut anchor = &mut self.root;而让 Some(ref mut node) = *anchor {锚点 = &mut node.next;}锚}}

(Rust 游乐场链接)>

然而,这失败了:

error[E0499]: 不能一次多次借用 `anchor.0` 作为可变的-->src/main.rs:14:24|14 |而让 Some(ref mut node) = *anchor {|^^^^^^^^^^^^^|||第二个可变借用发生在这里|第一个可变借用发生在这里...18 |}|- 第一次借阅到此结束错误[E0506]:无法分配给`anchor`,因为它是借用的-->src/main.rs:15:13|14 |而让 Some(ref mut node) = *anchor {|------------ 借用 `anchor` 出现在这里15 |锚点 = &mut node.next;|^^^^^^^^^^^^^^^^^^^^^^^ 对借用的`anchor` 的赋值发生在这里错误 [E0499]: 不能一次多次借用 `*anchor` 作为可变的-->src/main.rs:17:9|14 |而让 Some(ref mut node) = *anchor {|------------ 第一次可变借用发生在这里...17 |锚|^^^^^^ 第二个可变借位发生在这里18 |}|- 第一次借阅到此结束

这是有道理的,因为 anchornode 指的是相同的结构,但实际上我不再关心 anchor解构它.

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

解决方案

这是可能的...但我希望我有一个更优雅的解决方案.

诀窍不是借用anchor,而是在两个累加器之间进行切换:

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

这让我想到:

impl 递归{fn back(&mut self) ->&mut 链接{让 mut anchor = &mut self.root;环形 {让 tmp = 锚点;if let Some(ref mut node) = *tmp {锚点 = &mut node.next;} 别的 {锚= tmp;休息;}}锚}}

不是很漂亮,但这是借用检查器可以支持的东西,所以¯\_(ツ)_/¯.

@ker 通过创建一个未命名的临时文件对此进行了改进:

impl 递归{fn back(&mut self) ->&mut 链接{让 mut anchor = &mut self.root;环形 {匹配{锚点} {&mut Some(ref mut node) =>锚点 = &mut node.next,其他 =>返回其他,}}}}

这里的技巧是使用 {anchor} 移动 anchor 的内容到一个未命名的临时对象中,在该临时对象上执行匹配.因此,在 match 块中,我们不是从 anchor 借用,而是从临时借用,让我们可以自由修改 anchor.请参阅相关博客文章 Stuff the Identity函数确实(在 Rust 中).

I'm trying to navigate a recursive data structure iteratively in order to insert elements at a certain position. To my limited understanding, this means taking a mutable reference to the root of the structure and successively replacing it by a reference to its follower:

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
    }
}

(Rust playground link)

However, this fails:

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

This makes sense as both anchor and node refer to the same structure, but I actually don't care about anchor any more after destructuring it.

How could back() be implemented correctly using safe Rust?

解决方案

It is possible... but I wish I had a more elegant solution.

The trick is NOT to borrow from anchor, and therefore to juggle between two accumulators:

  • one holding the reference to the current node
  • the other being assigned the reference to the next node

This leads me to:

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
    }
}

Not exactly pretty, but this is something the borrow checker can get behind so ¯\_(ツ)_/¯.

@ker has improved on this by creating an unnamed temporary:

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,
            }
        }
    }
}

The trick here is that using {anchor} moves the content of anchor into an unnamed temporary on which the match executes. Therefore, in the match block we are not borrowing from anchor but from the temporary, leaving us free to modify anchor. See the related blog post Stuff the Identity Function Does (in Rust).

这篇关于迭代递归结构时无法获得可变引用:一次不能多次借用可变引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆