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

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

问题描述

我试图迭代导航递归数据结构,以便在某个位置插入元素.据我有限的理解,这意味着要可变引用结构的根,并依次用对它的跟随者的引用来替换它:

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()?

解决方案

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

诀窍是不要向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通过创建一个未命名的临时文件对此进行了改进:

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} moves anchor的内容移动到一个未命名的临时文件中,在该临时文件上执行匹配.因此,在match块中,我们不是从anchor借用,而是从临时借用的,这使我们可以自由修改anchor.参见相关博客文章填充身份函数可以(在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天全站免登陆