我可以在同一个函数中从 &mut self 移动到 &self 吗?

Can I move from &mut self to &self within the same function?

我正在尝试通过一个玩具应用程序学习一点 Rust,它涉及一个通过查询外部源动态填充的树数据结构。一开始,只有根节点存在。树结构提供了一种方法get_children(id),即returns一个[u32]所有节点的子节点的ID——要么这个数据是已知的,要么查询外部源,所有节点都是插入到树中。

我 运行 遇到了以下我似乎无法弄清楚的借用检查器问题:

struct Node {
    id: u32,
    value: u64, // in my use case, this type is much larger and should not be copied
    children: Option<Vec<u32>>,
}

struct Tree {
    nodes: std::collections::HashMap<u32, Node>,
}

impl Tree {
    fn get_children(&mut self, id: u32) -> Option<&[u32]> {
        // This will perform external queries and add new nodes to the tree
        None
    }

    fn first_even_child(&mut self, id: u32) -> Option<u32> {
        let children = self.get_children(id)?;
        let result = children.iter().find(|&id| self.nodes.get(id).unwrap().value % 2 == 0)?;
        Some(*result)
    }
}

这导致:

error[E0502]: cannot borrow `self.nodes` as immutable because it is also borrowed as mutable
  --> src/lib.rs:19:43
   |
18 |         let children = self.get_children(id)?;
   |                        ---- mutable borrow occurs here
19 |         let result = children.iter().find(|&id| self.nodes.get(id).unwrap().value % 2 == 0)?;
   |                                      ---- ^^^^^ ---------- second borrow occurs due to use of `self.nodes` in closure
   |                                      |    |
   |                                      |    immutable borrow occurs here
   |                                      mutable borrow later used by call

由于 get_children 可能会向树中插入节点,因此我们需要一个 &mut self 引用。然而,在我看来,children 的值已知后,self 不再需要可变借用。为什么这不起作用,我该如何解决?

编辑——我的解决方法

在 Chayim Friedman 的回答后,我决定不返回 Self。当首先调用 get_children 获取 ID 列表,然后使用 nodes.get() 获取相应的 Node 时,我主要 运行 遇到了上述问题。相反,我重构以提供以下功能:

impl Tree {
    fn load_children(&mut self, id: u32) {
        // If not present yet, perform queries to add children to the tree
    }

    fn iter_children(&self, id: u32) -> Option<IterChildren> {
        // Provides an iterator over the children of node `id`
    }
}

将可变引用降级为共享引用会生成一个应保持唯一的引用。这是必要的,例如Cell::from_mut(),具有以下签名:

pub fn from_mut(t: &mut T) -> &Cell<T>

此方法依赖于 &mut T 的唯一性保证,以确保仅通过 Cell 直接保留对 T 的引用。如果降级引用意味着可能违反了唯一性,则此方法将是不合理的,因为 Cell 中的值可能已被另一个共享引用更改(通过内部可变性)。

有关此内容的更多信息,请参阅 Common Rust Lifetime Misconceptions: downgrading mut refs to shared refs is safe

要解决这个问题,您需要从从可变引用创建的同一个共享引用中获取两个共享引用。例如,您还可以从 get_children():

return &Self
fn get_children(&mut self, id: u32) -> Option<(&Self, &[u32])> {
    // This will perform external queries and add new nodes to the tree
    Some((self, &[]))
}

fn first_even_child(&mut self, id: u32) -> Option<u32> {
    let (this, children) = self.get_children(id)?;
    let result = children.iter().find(|&id| this.nodes.get(id).unwrap().value % 2 == 0)?;
    Some(*result)
}