不能作为可变借用,因为它也作为不可变借用 [英] Cannot borrow as mutable because it is also borrowed as immutable

查看:65
本文介绍了不能作为可变借用,因为它也作为不可变借用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#[派生(调试)]结构节点{值:字符串,}#[派生(调试)]酒吧结构图{节点:Vec>,}fn mk_node(value: String) ->节点{节点{值}}pub fn mk_graph() ->图{图 { 节点:vec![] }}impl 图 {fn add_node(&mut self, value: String) {if let None = self.nodes.iter().position(|node| node.value == value) {让节点 = Box::new(mk_node(value));self.nodes.push(node);};}fn get_node_by_value(&self, value: &str) ->选项<&节点>{匹配 self.nodes.iter().position(|node| node.value == *value) {无 =>没有任何,一些(idx)=>self.nodes.get(idx).map(|n| &**n),}}}#[cfg(测试)]模组测试{使用超级::*;#[测试]fn some_test() {让 mut 图 = mk_graph();graph.add_node("source".to_string());graph.add_node("目的地".to_string());let source = graph.get_node_by_value("source").unwrap();让 dest = graph.get_node_by_value("destination").unwrap();graph.add_node("目的地".to_string());}}

(游乐场)

这里有错误

error[E0502]: 不能借用 `graph` 作为可变,因为它也被借为不可变-->src/main.rs:50:9|47 |let source = graph.get_node_by_value("source").unwrap();|----- 不可变借用发生在这里...50 |graph.add_node("目的地".to_string());|^^^^^ 可变借用发生在这里51 |}|- 不可变借用到此结束

Programming Rust 中的这个示例与我所拥有的非常相似,但它有效:

pub struct 队列 {old: Vec<char>,//较旧的元素,最后一个.Younger: Vec,//年轻元素,最年轻的最后.}实现队列{///将一个字符推到队列的后面.pub fn push(&mut self, c: char) {self.younger.push(c);}///从队列的前面弹出一个字符.如果///有一个要弹出的字符,则返回 `Some(c)`,如果队列为空,则返回 `None`.pub fn pop(&mut self) ->选项<字符>{如果 self.older.is_empty() {如果 self.younger.is_empty() {返回无;}//将较年轻的元素移至较老的元素,并将它们放入//承诺的顺序中.使用 std::mem::swap;交换(&mut self.older, &mut self.younger);self.older.reverse();}//现在老了肯定有东西.Vec 的 pop 方法//已经返回了一个 Option,所以我们已经设置好了.self.older.pop()}pub fn split(self) ->(Vec,Vec){(self.older, self.younger)}}酒吧 fn 主(){让 mut q = 队列 {旧:Vec::new(),年轻: Vec::new(),};q.push('P');q.push('D');assert_eq!(q.pop(), Some('P'));q.push('X');让(年长,年轻)= q.split();//q 现在未初始化.assert_eq!(旧的, vec!['D']);assert_eq!(年轻的,vec!['X']);}

解决方案

MCVE 您的问题可以简化为:

fn main() {让 mut items = vec![1];让 item = items.last();items.push(2);}

error[E0502]: 不能将 `items` 借为可变的,因为它也被借为不可变的-->src/main.rs:4:5|3 |让 item = items.last();|----- 不可变借用发生在这里4 |items.push(2);|^^^^^ 可变借用发生在这里5 |}|- 不可变借用到此结束

您遇到了正是 Rust 旨在防止的问题.您有一个指向向量的引用并试图插入到向量中.这样做可能需要重新分配向量的内存,从而使任何现有引用无效.如果发生这种情况并且您使用了 item 中的值,您将访问未初始化的内存,可能会导致崩溃.

在这种特殊情况下,您实际上并未使用item(或source,在原始版本中),因此您可以……. 不叫那条线.我假设您出于某种原因这样做,因此您可以将引用包装在一个块中,以便它们在您尝试再次改变值之前消失:

fn main() {让 mut items = vec![1];{让 item = items.last();}items.push(2);}

Rust 2018 不再需要这个技巧,因为 非词法生命周期已实现,但底层限制仍然存在——当有其他对同一事物的引用时,您不能拥有可变引用.这是 引用规则 包含在 Rust 编程语言 中.一个修改后的例子,仍然不适用于 NLL:

let mut items = vec![1];让 item = items.last();items.push(2);println!("{:?}", item);

在其他情况下,您可以复制或克隆向量中的值.该项目将不再是参考,您可以根据需要修改向量:

fn main() {让 mut items = vec![1];让 item = items.last().cloned();items.push(2);}

如果您的类型不可克隆,您可以将其转换为引用计数值(例如 RcArc) 然后可以克隆.您可能也可能不需要使用内部可变性:

struct NonClone;使用 std::rc::Rc;fn 主(){让 mut items = vec![Rc::new(NonClone)];让 item = items.last().cloned();items.push(Rc::new(NonClone));}

<块引用>

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

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

另见

I am learning Rust and I don't quite get why this is not working.

#[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());
    }
}

(playground)

This has the error

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

This example from Programming Rust is quite similar to what I have but it works:

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']);
}

解决方案

A MCVE of your problem can be reduced to this:

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

You are encountering the exact problem that Rust was designed to prevent. You have a reference pointing into the vector and are attempting to insert into the vector. Doing so might require that the memory of the vector be reallocated, invalidating any existing references. If that happened and you used the value in item, you'd be accessing uninitialized memory, potentially causing a crash.

In this particular case, you aren't actually using item (or source, in the original) so you could just... not call that line. I assume you did that for some reason, so you could wrap the references in a block so that they go away before you try to mutate the value again:

fn main() {
    let mut items = vec![1];
    {
        let item = items.last();
    }
    items.push(2);
}

This trick is no longer needed in Rust 2018 because non-lexical lifetimes have been implemented, but the underlying restriction still remain — you cannot have a mutable reference while there are other references to the same thing. This is one of the rules of references covered in The Rust Programming Language. A modified example that still does not work with NLL:

let mut items = vec![1];
let item = items.last();
items.push(2);
println!("{:?}", item);

In other cases, you can copy or clone the value in the vector. The item will no longer be a reference and you can modify the vector as you see fit:

fn main() {
    let mut items = vec![1];
    let item = items.last().cloned();
    items.push(2);
}

If your type isn't cloneable, you can transform it into a reference-counted value (such as Rc or Arc) which can then be cloned. You may or may not also need to use interior mutability:

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));
}

this example from Programming Rust is quite similar

No, it's not, seeing as how it doesn't use references at all.

See also

这篇关于不能作为可变借用,因为它也作为不可变借用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆