不能作为可变借用,因为它也作为不可变借用

问题描述 投票:0回答:3

我正在学习 Rust,但我不太明白为什么这不起作用。

#[derive(Debug)]
struct Node {
    value: String,
}

#[derive(Debug)]
pub struct Graph {
    nodes: Vec<Box<Node>>,
}

fn mk_node(value: String) -> Node {
    Node { value }
}

pub fn mk_graph() -> Graph {
    Graph { nodes: vec![] }
}

impl Graph {
    fn add_node(&mut self, value: String) {
        if let None = self.nodes.iter().position(|node| node.value == value) {
            let node = Box::new(mk_node(value));
            self.nodes.push(node);
        };
    }

    fn get_node_by_value(&self, value: &str) -> Option<&Node> {
        match self.nodes.iter().position(|node| node.value == *value) {
            None => None,
            Some(idx) => self.nodes.get(idx).map(|n| &**n),
        }
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn some_test() {
        let mut graph = mk_graph();

        graph.add_node("source".to_string());
        graph.add_node("destination".to_string());

        let source = graph.get_node_by_value("source").unwrap();
        let dest = graph.get_node_by_value("destination").unwrap();

        graph.add_node("destination".to_string());
    }
}

游乐场

这里有错误

error[E0502]: cannot borrow `graph` as mutable because it is also borrowed as immutable
  --> src/main.rs:50:9
   |
47 |         let source = graph.get_node_by_value("source").unwrap();
   |                      ----- immutable borrow occurs here
...
50 |         graph.add_node("destination".to_string());
   |         ^^^^^ mutable borrow occurs here
51 |     }
   |     - immutable borrow ends here

来自 Programming Rust 的这个示例与我的非常相似,但它有效:

pub struct Queue {
    older: Vec<char>,   // older elements, eldest last.
    younger: Vec<char>, // younger elements, youngest last.
}

impl Queue {
    /// Push a character onto the back of a queue.
    pub fn push(&mut self, c: char) {
        self.younger.push(c);
    }

    /// Pop a character off the front of a queue. Return `Some(c)` if there /// was a character to pop, or `None` if the queue was empty.
    pub fn pop(&mut self) -> Option<char> {
        if self.older.is_empty() {
            if self.younger.is_empty() {
                return None;
            }

            // Bring the elements in younger over to older, and put them in // the promised order.
            use std::mem::swap;
            swap(&mut self.older, &mut self.younger);
            self.older.reverse();
        }

        // Now older is guaranteed to have something. Vec's pop method // already returns an Option, so we're set.
        self.older.pop()
    }

    pub fn split(self) -> (Vec<char>, Vec<char>) {
        (self.older, self.younger)
    }
}

pub fn main() {
    let mut q = Queue {
        older: Vec::new(),
        younger: Vec::new(),
    };

    q.push('P');
    q.push('D');

    assert_eq!(q.pop(), Some('P'));
    q.push('X');

    let (older, younger) = q.split(); // q is now uninitialized.
    assert_eq!(older, vec!['D']);
    assert_eq!(younger, vec!['X']);
}
reference rust
3个回答
163
投票

您的问题的MRE可以简化为:

// This applies to the version of Rust this question
// was asked about; see below for updated examples.
fn main() {
    let mut items = vec![1];
    let item = items.last();
    items.push(2);
}
error[E0502]: cannot borrow `items` as mutable because it is also borrowed as immutable
 --> src/main.rs:4:5
  |
3 |     let item = items.last();
  |                ----- immutable borrow occurs here
4 |     items.push(2);
  |     ^^^^^ mutable borrow occurs here
5 | }
  | - immutable borrow ends here

此代码举例说明了 Rust 旨在防止的确切场景。您有一个指向向量的引用,并且正在尝试插入向量。这样做可能需要重新分配向量的内存,从而使任何现有引用无效。如果发生这种情况并且您使用了 item

 中的值,您将访问未初始化的内存,可能会导致崩溃。

在这种

特定情况下,您实际上并没有使用item

(或原版中的
source
),所以您可以......不调用该行。我假设您出于某种原因这样做了,因此您可以将引用包装在一个块中,以便它们在您尝试再次改变值之前消失:

fn main() { let mut items = vec![1]; { let item = items.last(); } items.push(2); }
在现代 Rust 中不再需要这个技巧,因为

非词法生命周期已经实现,但潜在的限制仍然存在——当存在对同一事物的其他引用时,你不能拥有可变引用。这是Rust 编程语言中介绍的参考规则之一。修改后的示例仍然不适用于 NLL:

let mut items = vec![1]; let item = items.last(); items.push(2); println!("{:?}", item);
在其他情况下,您可以复制或克隆向量中的值。该项目将不再是参考,您可以根据需要修改矢量:

fn main() { let mut items = vec![1]; let item = items.last().cloned(); items.push(2); }
如果您的类型不可克隆,您可以将其转换为可克隆的引用计数值(例如 

Rc

Arc
)。您可能也可能不需要使用
内部可变性

struct NonClone; use std::rc::Rc; fn main() { let mut items = vec![Rc::new(NonClone)]; let item = items.last().cloned(); items.push(Rc::new(NonClone)); }

Programming Rust 中的这个例子非常相似

不,不是,因为它根本不使用引用。

另请参阅

  • 不能借用`*x`作为可变的,因为它也被借用为不可变的
  • 根据最后一个元素将某些东西推入向量
  • 为什么可变借用的生命周期在函数调用完成后没有结束?
  • 我应该如何重构我的图形代码以避免“一次不能多次借用可变变量”错误?
  • 为什么我会收到错误“不能多次借用 x 作为可变对象”?
  • 为什么 Rust 想要一次多次借用一个可变变量?

11
投票
尝试将不可变的借用放入块 {...} 中。 这结束了区块之后的借用。

#[cfg(test)] mod tests { use super::*; #[test] fn some_test() { let mut graph = mk_graph(); graph.add_node("source".to_string()); graph.add_node("destination".to_string()); { let source = graph.get_node_by_value("source").unwrap(); let dest = graph.get_node_by_value("destination").unwrap(); } graph.add_node("destination".to_string()); } }
    

4
投票
因此,对于其他正在努力解决这个问题并想要快速解决问题的人来说,请使用克隆而不是引用。例如,我正在迭代此单元格列表并想要更改属性,因此我首先复制该列表:

let created = self.cells .into_iter() .map(|c| { BoardCell { x: c.x, y: c.y, owner: c.owner, adjacency: c.adjacency.clone(), } }) .collect::<Vec<BoardCell>>();
然后通过循环副本修改原始中的值:

for c in created { self.cells[(c.x + c.y * self.size) as usize].adjacency[dir] = count; }
使用 

Vec<&BoardCell>

 只会产生此错误。不确定这有多生锈,但是嘿,它有效。

© www.soinside.com 2019 - 2024. All rights reserved.