rust - ps4 - No se puede pedir prestada `x` como mutable más de una vez a la vez
rust traduccion (2)
En el siguiente código ( playground ):
struct Node {
datum: &''static str,
edges: Vec<Node>,
}
fn add<''a>(node: &''a mut Node, data: &''static str) -> &''a Node {
node.edges.push(Node {
datum: data,
edges: Vec::new(),
});
&node.edges[node.edges.len() - 1] // return just added one
}
fn traverse<F>(root: &Node, callback: &F)
where
F: Fn(&''static str),
{
callback(root.datum);
for node in &root.edges {
traverse(node, callback);
}
}
fn main() {
let mut tree = Node {
datum: "start",
edges: Vec::new(),
};
let lvl1 = add(&mut tree, "level1");
traverse(&mut tree, &|x| println!("{:}", x)); //I actually don''t need mutability here
}
Tengo este error:
error[E0499]: cannot borrow `tree` as mutable more than once at a time
--> src/main.rs:32:19
|
30 | let lvl1 = add(&mut tree, "level1");
| ---- first mutable borrow occurs here
31 |
32 | traverse(&mut tree, &|x| println!("{:}", x)); //I actually don''t need mutability here
| ^^^^ second mutable borrow occurs here
33 | }
| - first borrow ends here
Mi pregunta parece ser muy similar a ¿Por qué Rust quiere tomar prestada una variable como mutable más de una vez a la vez? , pero no estoy seguro. Si es así, ¿hay alguna solución para este caso?
El comportamiento es lógico. Considera qué
fn add<''a>(node: &''a mut Node, data: &''static str) -> &''a Node
medio.
Esto dice que
&mut Node
tiene una vida útil
igual
a la vida útil de su valor de retorno.
Debido a que asigna el valor de retorno a un nombre, este dura hasta el final del alcance.
Por lo tanto, el préstamo mutable también vive tanto tiempo.
Si puede descartar fácilmente el valor de retorno, hágalo. Puedes dejarlo caer al suelo:
let mut tree = Node {
datum: "start",
edges: Vec::new(),
};
add(&mut tree, "level1");
traverse(&mut tree, &|x| println!("{:}", x));
o puede usar un ámbito léxico para restringirlo sin soltarlo por completo.
Si desea pedir prestado el rendimiento sin obligar al préstamo mutable a vivir demasiado tiempo, probablemente tendrá que dividir la función en dos. Esto se debe a que no puede pedir prestado el valor de retorno del préstamo mutable para hacerlo.
Esto sucede debido a cómo se define
add
:
fn add<''a>(node: &''a mut Node, data: &''static str) -> &''a Node
Aquí se especifica que la vida útil de la referencia resultante debe ser igual a la vida útil de la referencia entrante. La única forma en que es posible (excepto el código inseguro) es que la referencia resultante se deriva de alguna manera de la referencia entrante, por ejemplo, hace referencia a algún campo dentro del objeto en el que los puntos de referencia entrantes son:
struct X {
a: u32,
b: u32,
}
fn borrow_a<''a>(x: &''a mut X) -> &''a mut u32 {
&mut x.a
}
Sin embargo, no hay forma de que el compilador sepa exactamente qué se toma prestado de la estructura entrante mirando solo la firma de la función (que, en general, es lo único que puede hacer al compilar código que usa esta función). Por lo tanto, no puede saber que el siguiente código es técnicamente correcto:
let mut x = X { a: 1, b: 2 };
let a = borrow_a(&mut x);
let b = &mut x.b;
Sabemos que
b
son disjuntos porque apuntan a diferentes partes de la estructura, pero el compilador no puede saberlo porque no hay nada en la firma de
borrow_a
que lo sugiera (y no puede haberlo, Rust no apoyarlo).
Por lo tanto, lo único sensato que el compilador podría hacer es considerar que se toma prestada
toda la
x
hasta que se
borrow_a()
la referencia devuelta por
borrow_a()
.
De lo contrario, sería posible crear dos referencias mutables para los mismos datos, lo que constituye una violación de las garantías de alias de Rust.
Tenga en cuenta que el siguiente código es correcto:
let mut x = X { a: 1, b: 2 };
let a = &mut x.a;
let b = &mut x.b;
Aquí el compilador
puede
ver que
b
nunca apuntan a los mismos datos, aunque sí apuntan dentro de la misma estructura.
No hay solución para esto, y la única solución sería reestructurar el código para que no tenga esos patrones de préstamo.