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

Reference 不能作为可变借用,因为它也是作为不可变借用的,reference,rust,Reference,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 {

我正在学习生锈,我不太明白为什么这不起作用

#[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());
    }
}
编程生锈的示例与我的示例非常相似,但它可以工作:

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']);
}
pub结构队列{
旧的:Vec,//旧的元素,最旧的最后一个。
年轻的:Vec,//年轻的元素,最年轻的最后一个。
}
impl队列{
///将字符推到队列的后面。
发布fn推送(&M-self,c:char){
自我。年轻。推(c);
}
///从队列前面弹出一个字符。如果有///要弹出的字符,则返回'Some(c)';如果队列为空,则返回'None'。
pub fn pop(&mut self)->选项{
如果self.older.is_为空(){
如果self.younger.是空的(){
不返回任何值;
}
//将年轻的元素带到年长的元素,并按承诺的顺序排列。
使用std::mem::swap;
交换(&mut self.older,&mut self.younger);
self.older.reverse();
}
//现在,older肯定会有一些东西。Vec的pop方法//已经返回了一个选项,所以我们设置好了。
self.older.pop()
}
发布fn拆分(自)->(Vec,Vec){
(self.年长,self.年轻)
}
}
pub fn main(){
设mut q=Queue{
旧版本:Vec::new(),
杨格:Vec::new(),
};
q、 推动('P');
q、 推动('D');
assert_eq!(q.pop(),Some('P');
q、 推动('X');
let(年长、年轻)=q.split();//q现在未初始化。
assert_eq!(旧的,vec!['D']);
assert_eq!(younger,vec!['X']);
}
A您的问题可以归结为:

fn main() {
    let mut items = vec![1];
    let item = items.last();
    items.push(2);
}
error[E0502]:无法将'items'作为可变项借用,因为它也是作为不可变项借用的
-->src/main.rs:4:5
|
3 | let item=items.last();
|----此处发生不可变借用
4 |项。推送(2);
|^^^^^此处发生可变借用
5 | }
|-不可变的借阅到此结束
你遇到的正是防锈设计的目的。您有一个指向向量的引用,并且正在尝试插入向量。这样做可能需要重新分配向量的内存,从而使任何现有引用无效。如果发生这种情况,并且您使用了
项中的值
,您将访问未初始化的内存,这可能会导致崩溃

在这种特殊情况下,您实际上没有使用
(或原始版本中的
源代码
),因此您可以。。。不要打那个电话。我假设您是出于某种原因这样做的,因此您可以将引用包装在一个块中,以便在您再次尝试变异值之前它们消失:

fn main() {
    let mut items = vec![1];
    {
        let item = items.last();
    }
    items.push(2);
}
在Rust 2018中不再需要这个技巧,因为它已经实现了,但是基本的限制仍然存在——当存在对同一事物的其他引用时,不能有可变引用。这是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);
}
如果您的类型不可克隆,则可以将其转换为引用计数值(如或),然后可以克隆该值。您可能需要也可能不需要使用:

编程生锈的这个例子非常相似

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

另见

尝试将您的不可变借用放在块{…}中。 这将结束块后的借用

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

为什么编程生锈的例子会不一样呢?我不太明白。我想它也可以写成
(&mut q).push('D')
(&q).pop
,它确实借用了
q
作为可变引用,然后是不可变引用。好的,我想我现在明白了。stackoverflow.com/q/36565833/155423这就回答了为什么。@在您的示例中,Shepmaster不是返回引用的last(),而是返回引用的last()。如果它移出了该值怎么办?那么vec中就不会有引用,不可变的借阅将在语句之后结束?(这只是一个假设的场景。如果它返回一个拥有的值,..last()应该是&而不是&mut)@SujitJoshi,你的意思不明确,但是如果它是
fn last(self)->Option
fn last(&mut self)->Option
(又称
pop
),那么是的,这很好,因为返回值不会因为进一步改变向量而无效。
#[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());
    }
}