web-dev-qa-db-ja.com

不変としても借用されているため、可変として借用できません

私は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

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

問題の [〜#〜] mcve [〜#〜] は、これに還元できます。

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の値を使用すると、初期化されていないメモリにアクセスして、クラッシュを引き起こす可能性があります。

このparticularの場合、実際にはitem(または元のsource)を使用していないので、...その行を呼び出せないだけです。私はあなたが何らかの理由でそれをしたと思うので、値を再び変更しようとする前に参照が消えるように参照をブロックにラップすることができます:

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

non-lexical lifetime であったため、このトリックはRust 2018では不要になりました実装されていますが、基本的な制限は残っています。同じものへの他の参照がある間は、可変参照を持つことはできません。これは、 参照の規則 の1つですThe Rust Programming Language。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);
}

タイプがクローン可能でない場合は、参照カウント値( RcArc など)に変換してから、クローンを作成できます。 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));
}

プログラミングのこの例Rustは非常に似ています

いいえ、そうではありません。参照をまったく使用しないように見えます。

こちらもご覧ください

17
Shepmaster

不変の借用をブロック{...}内に配置してください。これにより、ブロックの後の借用が終了します。

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