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.