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

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

问题描述

我正在学习 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']);}

解决方案

MRE 您的问题可以简化为:

//这个问题适用于Rust的版本//被问到;有关更新的示例,请参见下文.fn 主(){让 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 不再需要这个技巧,因为 非词法生命周期已实现,但底层限制仍然存在——当有其他对同一事物的引用时,您不能拥有可变引用.这是 引用规则.一个修改后的例子,仍然不适用于 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 MRE of your problem can be reduced to this:

// 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

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 modern Rust because non-lexical lifetimes have been implemented, but the underlying restriction still remains — 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天全站免登陆