reference rust

reference - No se puede tomar prestado como mutable porque también se toma prestado como inmutable



rust (2)

Intente poner su préstamo inmutable dentro de un bloque {...}. Esto termina el préstamo después del bloque.

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

Estoy aprendiendo Rust y no entiendo por qué esto no funciona.

#[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 )

Esto tiene el 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

Este ejemplo de Programming Rust es bastante similar a lo que tengo pero funciona:

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


Un MCVE de su problema puede reducirse a esto:

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

Estás encontrando el problema exacto que Rust fue diseñado para prevenir. Tiene una referencia apuntando al vector y está intentando insertar en el vector. Hacerlo podría requerir que la memoria del vector se reasigne, invalidando cualquier referencia existente. Si eso sucediera y utilizara el valor en el item , estaría accediendo a la memoria no inicializada, lo que podría causar un bloqueo.

En este caso en particular , en realidad no está utilizando el item (o la source , en el original), por lo que podría ... no llamar a esa línea. Supongo que lo hiciste por alguna razón, por lo que podrías envolver las referencias en un bloque para que desaparezcan antes de intentar volver a mutar el valor:

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

Este truco ya no es necesario en Rust 2018 porque se han implementado vidas no léxicas , pero la restricción subyacente aún permanece: no puede tener una referencia mutable mientras haya otras referencias a la misma cosa. Esta es una de las reglas de referencia cubiertas en The Rust Programming Language . Un ejemplo modificado que todavía no funciona con NLL:

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

En otros casos, puede copiar o clonar el valor en el vector. El elemento ya no será una referencia y puede modificar el vector como mejor le parezca:

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

Si su tipo no se puede clonar, puede transformarlo en un valor contado de referencia (como Rc o Arc ) que luego se puede clonar. Es posible que también necesite o no usar mutabilidad interior :

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

Este ejemplo de Programming Rust es bastante similar

No, no lo es, ya que no utiliza referencias en absoluto.

Ver también

  • No se puede pedir prestado `* x` como mutable porque también se toma prestado como inmutable
  • Empujar algo en un vector dependiendo de su último elemento
  • ¿Por qué no termina la vida útil de un préstamo mutable cuando se completa la llamada a la función?
  • ¿Cómo debo reestructurar mi código gráfico para evitar un error "No se puede tomar prestada la variable como mutable más de una vez a la vez"?
  • ¿Por qué aparece el error "no se puede pedir prestado x como mutable más de una vez"?
  • ¿Por qué Rust quiere pedir prestada una variable como mutable más de una vez a la vez?