Rust:需要在迭代中获得一个可变引用的Self

7

我在Rust中有一个图数据结构:

type NodeIndex = usize;

struct Graph {
    nodes: Vec<NodeIndex>,
    edges: Vec<(NodeIndex, NodeIndex)>,
}

我想迭代函数内的所有节点,并调用一个使用每个节点作为元素来改变图形的函数,例如:

impl Graph {
    fn mutate_fn(&mut self) {
        for node in self.nodes {
            self.mutate_using_node(node);
        }
    }

    fn mutate_using_node(&mut self, node: NodeIndex) {
        // mutate self here
    }
}

这种方式行不通,因为我会有超过一个可变引用。我也不能传递&self,因为那样我将会有一个可变引用和一个不可变引用。在Rust中如何处理这个问题?


1
这个回答解决了你的问题吗?为什么通过提取方法进行重构会触发借用检查器错误? - Stargateur
mutate_using_node函数读取self的哪些部分,并对其进行了什么样的变异? - Solomon Ucko
1
@SolomonUcko 它只读取和改变边缘变量。将'mutate_using_node'重写为一个静态方法,该方法接受可变引用到边缘和我们当前迭代的节点,这样做是否有意义? - eager2learn
2
我建议阅读Niko关于过程间冲突的博客文章 - 它涵盖了解决这种问题的几个通用解决方案。 - Sven Marnach
@eager2learn 直到方法的部分借用(partial borrowing)被实现,这可能是最简单和最有效的解决方案。您也可以尝试使用 partial_ref crate,但我对它不太了解。 - Solomon Ucko
1个回答

1

好的,您确实无法这样做。一般情况下,我可以提供两种主要方法,并针对您的示例进行解释。

拆分借用

这种方法可能是其他方法中最困难和/或最慢的方法。只需按照借用检查器的要求操作:不要混淆可变和不可变的借用。对于您的情况,可以简单地在mutate_fn中克隆节点:

let nodes = self.nodes.clone();
for node in nodes {
    self.mutate_using_node(node);
}

很难在没有详细信息的情况下进行推理,但我认为这是该方法唯一的方式。例如,如果您只更改边缘,就像这样:
fn mutate_using_node(&mut self, node: NodeIndex) {
    for e in &mut self.edges {
        if e.0 == node {
            std::mem::swap(&mut e.0, &mut e.1);
        }
    }
}

你可以通过将这些功能结合起来来简单地处理它:

for node in self.nodes.iter().copied() {
    for e in &mut self.edges {
        if e.0 == node {
            std::mem::swap(&mut e.0, &mut e.1);
        }
    }
}

总的来说,没有一个终极的逐步指南(除了复制),可以将代码分离。这取决于代码语义。

内部可变性

这就是RefCell的作用。它基本上在运行时处理借用检查规则,如果这些规则被破坏,就会出现恐慌。对于像这样的情况:

use std::cell::RefCell;

type NodeIndex = usize;

struct Graph {
    nodes: RefCell<Vec<NodeIndex>>,
    edges: RefCell<Vec<(NodeIndex, NodeIndex)>>,
}

fn mutate_fn(&self) {
    for &node in self.nodes.borrow().iter() {
        self.mutate_using_node(node);
    }
}

fn mutate_using_node(&self, node: NodeIndex) { // <- notice immutable ref
    for e in self.edges.borrow_mut().iter_mut() {
        if e.0 == node {
            std::mem::swap(&mut e.0, &mut e.1);
        }
    }
}

记住,RefCell 不是 Sync,因此它不能在线程之间共享。对于涉及到线程的情况,MutexRwLock 是一种替代方案。

好的,你也可以使用不安全的代码来解决这个问题。但是你可能不想这样做。 - Kitsu
谢谢你的回答。不过编译器报错,说 Vec<usize> 没有 borrow_mut() 方法。 - eager2learn
哎呀,错过了 Graph 的定义,已添加。 - Kitsu

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