rust immutability mutability

rust - no puede tomar prestado `self.x` como inmutable porque`*self` también se toma prestado como mutable



immutability mutability (1)

Primero, deja que el código hable:

#[derive(Debug)] struct Bar; #[derive(Debug)] struct Qux { baz: bool } #[derive(Debug)] struct Foo { bars: Vec<Bar>, qux: Qux, } impl Foo { fn get_qux(&mut self) -> &mut Qux { &mut self.qux } fn run(&mut self) { // 1. Fails: let mut qux = self.get_qux(); // 2. Works: // let mut qux = &mut Qux { baz: false }; // 3. Works: // let mut qux = &mut self.qux; let qux_mut = &mut qux; qux_mut.baz = true; for bar in &self.bars { println!("{:?}", bar); } } } fn main() { println!("Hello, world!"); let mut foo = Foo { bars: vec!(), qux: Qux { baz: false } }; foo.run(); }

Este error:

error[E0502]: cannot borrow `self.bars` as immutable because `*self` is also borrowed as mutable --> src/main.rs:33:21 | 22 | let mut qux = self.get_qux(); | ---- mutable borrow occurs here ... 33 | for bar in &self.bars { | ^^^^^^^^^ immutable borrow occurs here ... 36 | } | - mutable borrow ends here

Si descomento 2. o 3. , ¿por qué se compila bien? La función llamada en 1. no hace nada drásticamente diferente de 2. o 3. .. Entonces, ¿por qué es que 1. no se compila?

Aunque hay muchas preguntas tituladas similares , no pude identificar esto claramente como un engaño (aparte del mensaje de error que es el mismo), posiblemente debido a mi falta de comprensión del sistema de propiedad / préstamo en Rust.


En Rust, el compilador se detiene en el límite de llamada de función al evaluar parámetros genéricos, que incluyen parámetros genéricos de por vida. En su caso 1, está llamando a un método:

fn get_qux(&mut self) -> &mut Qux { &mut self.qux }

Esta función indica que todo el self tomará prestado de forma mutable y que la referencia devuelta vivirá tanto tiempo como self . Durante este tiempo, no se pueden hacer otros préstamos (mutables o no) de uno mismo o de sus componentes.

En su segundo caso, crea un Qux completamente nuevo que no tiene ningún apego a su estructura. No es un gran ejemplo, porque tiene un significado muy diferente. Si este caso funciona para usted, debe hacerlo . Sin embargo, no modificará lo mismo que en el caso 1.

En el tercer caso, evita la llamada a la función. Eso significa que el compilador tiene un poco más de información sobre lo que se toma prestado. Específicamente, puede ver que self.qux no interactúa en absoluto con self.bars , por lo que no hay error.

Puede hacer que su ejemplo original funcione agregando un nuevo alcance:

fn run(&mut self) { { let mut qux = self.get_qux(); let qux_mut = &mut qux; qux_mut.baz = true; } for bar in &self.bars { println!("{:?}", bar); } }

Aquí, el alcance artificial define claramente dónde termina el préstamo mutable. Una vez que finaliza el préstamo, otros artículos pueden hacer nuevos préstamos.

Si necesita modificar qux dentro del bucle, entonces debe seguir el tercer patrón:

let mut qux = &mut self.qux; for bar in &self.bars { qux.baz = ! qux.baz; println!("{:?}", bar); }

O lo más simple:

for bar in &self.bars { self.qux.baz = ! self.qux.baz; println!("{:?}", bar); }

Muchas veces, puede refactorizar su código para crear nuevas estructuras que tengan información y encapsular un buen límite de mutación para crear un código como este.