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?