在调用`&'a mut self`方法后,无法多次将变量作为可变借用。

3

我在使用Graph对象时遇到了生命周期/借用方面的问题。

fn main() {
    let mut g = Graph {
        nodePointer: &mut 0,
        edgePointer: &mut 0,
        nodes: &mut Vec::new(),
        edges: &mut Vec::new(),
    };
    let node1 = g.add_node((1, 1));
    let node2 = g.get_node(0);
}

pub struct Graph<'a> {
    pub nodePointer: &'a mut usize,
    pub edgePointer: &'a mut usize,
    pub nodes: &'a mut Vec<Node>,
    pub edges: &'a mut Vec<Edge>,
}

impl<'a> Graph<'a> {
    pub fn add_node(&'a mut self, data: (u64, u64)) -> usize {
        let id: usize = *self.nodePointer;
        self.nodes.push(Node {
            id: id,
            datum: data,
        });
        *self.nodePointer += 1;
        return id;
    }

    pub fn get_node(&'a mut self, id: usize) -> &'a Node {
        return &self.nodes[id];
    }

    pub fn add_edge(&'a mut self, source: u64, target: u64, weight: u16) -> usize {
        let id: usize = *self.nodePointer;
        self.edges.push(Edge {
            id: id,
            source,
            target,
            weight,
        });
        *self.edgePointer = *self.edgePointer + 1;
        return id;
    }
}

pub struct Node {
    pub id: usize,
    pub datum: (u64, u64),
}

pub struct Edge {
    pub id: usize,
    pub source: u64,
    pub target: u64,
    pub weight: u16,
}

error[E0499]: cannot borrow `g` as mutable more than once at a time
  --> src/main.rs:9:17
   |
8  |     let node1 = g.add_node((1, 1));
   |                 - first mutable borrow occurs here
9  |     let node2 = g.get_node(0);
   |                 ^ second mutable borrow occurs here
10 | }
   | - first borrow ends here

这是一个奇怪的问题。从逻辑上讲,add_node 应该返回 id 的副本,并且不应该留下未解决的借用问题... - MutantOctopus
等一下,我明白你的问题了。给我一点时间来写出解决方案。 - MutantOctopus
1
与其只提供你认为重要的一行代码而没有任何上下文,最好在你的问题中包含完整的错误信息。你的示例也可以更短:如果你删除有关边缘的所有内容,你会得到相同的错误。 - mcarton
你为什么要拥有这些引用呢?为什么不将它们变成值成员? - Sebastian Redl
2个回答

5

您的问题源于生命周期的误用,具体来说是在您的add_node签名中:

pub fn add_node(&'a mut self, data: (u64, u64)) -> usize

在这个签名中,你声明 add_node 需要在 Graph < 'a > 上采取一个可变借用的&'a mut self;换句话说,你告诉Rust这个方法需要在整个Graph的生命周期'a结束之前采取对图形的可变借用,该借用不能被丢弃。但由于图形本身持有对图形的引用,因此仅当图形本身被丢弃时,该引用才会被丢弃。
由于add_node不需要返回结构体中任何对象的引用,因此保留该借用是无关紧要的。如果你更改你的add_node方法以删除显式生命周期,则:
pub fn add_node(&mut self, data: (u64, u64)) -> usize

然后你的示例不再引发错误,因为add_node现在仅借用self直到函数完成。(实际上,在幕后,这会有效地创建第二个生命周期'b并将签名变成&'b mut self)

请查看演示版进行验证。


顺便说一句,我不认为其他方法中的 &'a mut self 是必要的。 'a 需要反映返回值的生命周期,而不是图形本身的借用。但我可能是错的。 - MutantOctopus
这个回复很棒!解决方案有效且说明详细。 - Nathan Horrigan
1
@NathanHorrigan 虽然这个解决方案可行,但我要提醒你对我的“为什么”解释持保留态度——我承认这只是对 Rust 功能的猜测,而不是我的专业领域。 - MutantOctopus

1
考虑以下内容:
struct Foo<'a> {
    x: &'a i32,
}

作为书籍所述:

那么为什么我们需要在这里使用lifetime呢?我们需要确保对Foo的任何引用都不会超过它包含的i32的引用。

如果你写了类似于:

impl<'a> Graph<'a> {
    pub fn add_node(&'a mut self, data: (u64, u64)) -> usize {
        ...

生命周期声明&'a mut self并不是为了将Graph实例的生命周期与其中包含的引用相关联,而是为了声明对于可变的self引用,持有与Graph字段引用相同的生命周期'a
fn main() {    
    let mut g = Graph {                          // <------------
        nodePointer: &mut 0,                     //             |            
        edgePointer: &mut 0,                     // lifetime    |
        nodes: &mut Vec::new(),                  // of Graph    | 'a
        edges: &mut Vec::new(),                  // references  |
    };                                           //             |
    let node1 = Graph::add_node(&mut g, (1, 1)); //             |   
    let node2 = Graph::get_node(&mut g, 0);      //             |
}                                                //<-------------

g.get_node(0)重写为Graph::get_node(&mut g, 0)只是为了明确地暴露出&mut引用。

'a的生命周期来看,很明显&mut g引用被可变地借用了多次,这导致了错误。


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