我正在尝试实现一个系统,该系统将使用借用检查/生命周期来提供集合上安全的自定义索引。考虑以下代码:
struct Graph(i32);
struct Edge<'a>(&'a Graph, i32);
impl Graph {
pub fn get_edge(&self) -> Edge {
Edge(&self, 0)
}
pub fn split(&mut self, Edge(_, edge_id): Edge) {
self.0 = self.0 + edge_id;
}
pub fn join(&mut self, Edge(_, edge0_id): Edge, Edge(_, edge1_id): Edge) {
self.0 = self.0 + edge0_id + edge1_id;
}
}
fn main() {
let mut graph = Graph(0);
let edge = graph.get_edge();
graph.split(edge)
}
当调用像split
或join
这样的方法时,应该删除对Edge
结构体所借用的图形的引用。这将实现API不变量,即在图形变异时必须销毁所有边索引。然而,编译器并不理解它。它会出现类似以下的错误消息:
error[E0502]: cannot borrow `graph` as mutable because it is also borrowed as immutable
--> src/main.rs:23:5
|
22 | let edge = graph.get_edge();
| ----- immutable borrow occurs here
23 | graph.split(edge)
| ^^^^^ mutable borrow occurs here
24 | }
| - immutable borrow ends here
如果我理解正确的话,编译器无法意识到在边结构中发生的图形借用实际上是在调用函数时被释放的。有没有办法让编译器知道我在这里尝试做什么?
额外的问题:有没有一种方法可以完全不借用Edge结构中的图形来完成相同的操作?边缘结构仅用于遍历的临时目的,永远不会成为外部对象状态的一部分(我有'weak'版本的边缘处理)。
补充说明:经过一些挖掘,似乎离解决问题还有很远的路要走。首先,Edge(_,edge_id)实际上并没有解构Edge,因为_根本没有被绑定(是的,i32是复制的,这使事情变得更加复杂,但这很容易通过将其包装到非复制结构中来解决)。其次,即使我完全解构了Edge(即通过在单独的作用域中进行操作),图形的引用仍然存在,尽管它应该已经移动了(这可能是一个bug)。只有在单独的函数中执行解构才有效。现在,我有一个想法如何规避它(通过使用一个描述状态更改的单独对象,并根据提供的索引解构它们),但这很快变得非常笨拙。
get_edge()
函数通过&self
返回了一个不可变引用,所以只要edge1
存在,不可变借用就会存在。借用检查器正在按预期工作。我不太确定为什么您想返回对自身的引用,而您想要的只是边缘 ID。 - user3704639Edge
实例可以存在。注意:我实际上会在这里使用PhantomData<&Graph>,但这并不改变问题。 - MrMobster