首页 文章

在&mut self方法中展开成员变量时,不能移出借来的内容

提问于
浏览
2

我试图在Rust中创建一个Disjoint-Set数据结构 . 相关代码是:

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> 但我总是遇到同样的错误 .

这是什么错误,我该如何解决?

2 回答

  • 3

    这个匹配臂将按值获取枚举变量组件 . 由于您的类型不可复制,这意味着组件将移出原始位置 . 这会使你的原始结构部分未定义 - 在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以查看一种解决方法 .

  • 2

    使用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()
                }
            }
        }
    }
    

相关问题