在 &mut self 方法中,当解包成员变量时无法移动借用内容。

6

我正在尝试使用Rust创建一个不相交集数据结构。相关代码如下:

pub struct Set<'a, T: 'a> {
    rank: u32,
    value: T,
    parent: Option<&'a mut Set<'a, T>>,
}

impl<'a, T> Set<'a, T> {
    pub fn find(&'a mut self) -> &'a mut Set<'a, T> {
        match self.parent {
            None => self,
            Some(mut p) => {
                self.parent = Some(p.find());
                self.parent.unwrap()
            }
        }
    }
}

我得到的错误是:
error[E0507]: cannot move out of borrowed content
  --> src/main.rs:9:15
   |
9  |         match self.parent {
   |               ^^^^ cannot move out of borrowed content
10 |             None => self,
11 |             Some(mut p) => {
   |                  ----- hint: to prevent move, use `ref p` or `ref mut p`

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:13:17
   |
13 |                 self.parent.unwrap()
   |                 ^^^^ cannot move out of borrowed content

我不确定我完全理解了借用检查器,但是我正在使用引用来避免对结构体本身进行所有权控制,以便它们可以被指向和重新分配,类似于其他语言中的操作。

如果我从结构体的引用中删除 mut,就可以避免这些错误,但是因为它们是不可变的,所以我无法更改每个集合的父级。

我已经阅读了类似的问题,例如:

这些并没有帮助我解决这个问题。我还尝试过重构函数 find 以及结构体本身,使用 Rc<RefCell<Set>>Box<Set>,但最终总是出现相同的错误。

这个错误是什么,我该如何修复它?

1
这是一个*完全相同的副本,因为两个问题都与unwrap消耗Option的事实有关。 - Shepmaster
@Shepmaster 我尝试过那样做,但是我收到一个错误提示我不能赋值给 self.parent,因为它已经被借用了。在第二个错误上使用 as_mut() 会使我返回一个对结构体的可变引用的可变引用,这似乎也行不通。即使尝试将此函数分解为每个步骤来完成也似乎没有解决问题。 - mcdonalda1993
你可能也对Rust中的手动锁定感兴趣,有人也在尝试实现并查集/不相交集合。 - Shepmaster
2个回答

4
这个匹配的 arm 将会通过值获取枚举变体组件。由于您的类型不可复制,这意味着该组件将被移出原始位置。这将使您的原始结构部分未定义 - 在 Rust 中是不可接受的。
为了解决这个问题,建议像编译器建议的那样使用引用:
Some(ref mut p) =>

下一步,不要将结果存储在Option中,然后立即取回,而是尝试将引用保存在变量中,将其放入Option并返回:
let z = p.find();
self.parent = Some(z);
z

这就是整个想法的核心问题所在:
error[E0499]: cannot borrow `*z` as mutable more than once at a time
  --> src/main.rs:14:17
   |
13 |                 self.parent = Some(z);
   |                                    - first mutable borrow occurs here
14 |                 z
   |                 ^ second mutable borrow occurs here
15 |             }
   |             - first borrow ends here

你正在尝试存储可变引用并返回它。这意味着对同一项会有多个并发的可变引用(也称别名)。防止这种情况是 Rust 安全系统的另一个核心原则,因为这样编译器更难保证何时和在哪里更改事物。
查看this answer以了解解决此问题的一种方法。

3

Option::take 用作 match self.parent.take(),这是在这种情况下的一种基本惯用语。

self.parent.unwrap() 表达式也会导致错误;为此,您需要解决 unwrap 消耗 self 的事实,使用 Option::as_mut 编写 self.parent.as_mut().unwrap() 以使 unwrap 消耗一个引用而不是一个值。

最终代码如下:

pub struct Set<'a, T: 'a> {
    rank: u32,
    value: T,
    parent: Option<&'a mut Set<'a, T>>,
}

impl<'a, T> Set<'a, T> {
    pub fn find(&'a mut self) -> &'a mut Set<'a, T> {
        match self.parent.take() {
            None => self,
            Some(p) => {
                self.parent = Some(p.find());
                self.parent.as_mut().unwrap()
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接