¿Qué significa el error en este caso?
fn main() {
let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
v[v[1]] = 999;
}
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/main.rs:3:7
|
3 | v[v[1]] = 999;
| --^----
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
| mutable borrow later used here
He encontrado que la indexación se realiza a través del Index
e IndexMut
rasgos y que v[1]
es el azúcar sintáctica para *v.index(1)
. Equipado con este conocimiento, intenté ejecutar el siguiente código:
use std::ops::{Index, IndexMut};
fn main() {
let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
*v.index_mut(*v.index(1)) = 999;
}
Para mi sorpresa, ¡esto funciona perfectamente! ¿Por qué el primer fragmento no funciona, pero el segundo sí? Según entiendo la documentación, deberían ser equivalentes, pero obviamente este no es el caso.
rust
borrow-checker
Lucas Boucke
fuente
fuente
Respuestas:
La versión desugared es ligeramente diferente de la que tienes. La línea
en realidad desugar a
Esto da como resultado el mismo mensaje de error, pero las anotaciones dan una pista de lo que está sucediendo:
La diferencia importante con su versión desugared es el orden de evaluación. Los argumentos de una llamada a la función se evalúan de izquierda a derecha en el orden indicado, antes de realizar la llamada a la función. En este caso, esto significa que primero
&mut v
se evalúa, pidiendo prestado de forma mutablev
. A continuación,Index::index(&v, 1)
debe evaluarse, pero esto no es posible:v
ya está prestado de manera mutable. Finalmente, el compilador muestra que la referencia mutable todavía es necesaria para la llamada a la funciónindex_mut()
, por lo que la referencia mutable todavía está viva cuando se intenta la referencia compartida.La versión que realmente compila tiene un orden de evaluación ligeramente diferente.
Primero, los argumentos de la función para las llamadas al método se evalúan de izquierda a derecha,
*v.index(1)
es decir, se evalúa primero. Esto da como resultado ausize
, y el préstamo compartido temporal dev
se puede liberar nuevamente. Luego,index_mut()
se evalúa el receptor de ,v
es decir, se toma prestado de forma mutable. Esto funciona bien, ya que el préstamo compartido ya se ha finalizado, y toda la expresión pasa el corrector de préstamo.Tenga en cuenta que la versión que compila solo lo hace desde la introducción de "vidas no léxicas". En versiones anteriores de Rust, el préstamo compartido viviría hasta el final de la expresión y daría lugar a un error similar.
La solución más limpia en mi opinión es usar una variable temporal:
fuente
*v.index_mut(*v.index_mut(1)) = 999;
falla con "no se puede pedir prestado v como mutable más de una vez" ~> no debería ser el compilador, como*v.index_mut(*v.index(1)) = 999;
capaz de darse cuenta de que el préstamo interno ya no es necesario?*v.index(1)
es el valor almacenado en ese índice, y ese valor no requiere mantenerv
vivo el préstamo . El resultado de*v.index_mut(1)
, por otro lado, es una expresión de lugar mutable que teóricamente podría asignarse, por lo que mantiene vivo el préstamo. En la superficie, debería ser posible enseñarle al verificador de préstamos que una expresión de lugar en un contexto de expresión de valor puede tratarse como una expresión de valor, por lo que es posible que esto se compile en alguna versión futura de Rust.{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }