rust lifetime

rust - ¿La variable movida sigue tomando prestado después de llamar a ''drop''?



lifetime (2)

fn main() { let mut x: Vec<&i32> = vec![]; let a = 1; x.push(&a); drop(x); // x.len(); // error[E0382]: use of moved value: `x` } // `a` dropped here while still borrowed

El compilador sabe que drop() cae x (como se desprende del error en el código comentado) pero aún piensa que la variable está tomando prestado de a ! ¡Esto es injusto!

¿Debería considerarse esto como uno de los numerosos engaños de rust-lang/rust#6393 (que ahora es rastreado por rust-lang/rfcs#811 ?) Pero la discusión allí parece centrarse en hacer que &mut self y &self coexistan en un solo bloquear.


El compilador sabe que drop() cae x (como se desprende del error en el código comentado)

El compilador Rust no sabe nada sobre drop y lo que hace. Es solo una función de biblioteca, que podría hacer lo que quiera con el valor, ya que ahora lo posee.

La definición de drop , como señala la documentación, es literalmente simplemente:

fn drop<T>(_x: T) { }

Funciona porque el argumento se mueve a la función y, por lo tanto, el compilador lo descarta automáticamente cuando finaliza la función.

Si crea su propia función, obtendrá exactamente el mismo mensaje de error:

fn my_drop<T>(_x: T) { } fn main() { let mut x: Vec<&i32> = vec![]; let a = 1; x.push(&a); my_drop(x); x.len(); }

Esto es exactamente lo que se entiende en la documentación cuando dice que la drop "no es mágica" .


No puedo darte una respuesta definitiva, pero intentaré explicarte algunas cosas aquí. Comencemos aclarando algo:

El compilador sabe drop() drops x

Esto no es verdad. Si bien hay algunas cosas "mágicas" en la biblioteca estándar que el compilador conoce, drop() no es un elemento tan lang . De hecho, podría implementar drop() usted mismo y en realidad es lo más fácil de hacer:

fn drop<T>(_: T) {}

La función simplemente toma algo por valor (por lo tanto, se mueve a drop() ) y como no sucede nada dentro de drop() , este valor se elimina al final del ámbito, como en cualquier otra función. Entonces: el compilador no sabe que x se descarta, solo sabe que x se mueve.

Como habrás notado, el error del compilador permanece igual independientemente de si agregamos o no la llamada drop() . En este momento, el compilador solo mirará el alcance de una variable cuando se trata de referencias. De la introducción de Niko Matsakis a NLL :

La forma en que funciona actualmente el compilador, asignando una referencia a una variable significa que su vida útil debe ser tan grande como el alcance completo de esa variable.

Y en una publicación posterior de su blog :

En particular, hoy, una vez que toda la vida debe extenderse más allá de los límites de una sola [...] declaración, debe extenderse hasta el final del bloque de cierre.

Esto es exactamente lo que sucede aquí, así que sí, su problema tiene que ver con todas estas cosas de "préstamos léxicos". Desde la perspectiva de los compiladores actuales, la vida útil de la expresión &a debe ser al menos tan grande como el alcance de x . Pero esto no funciona, ya que la referencia sobreviviría a a , ya que el alcance de x es mayor que el alcance de a como lo señala el compilador:

= note: values in a scope are dropped in the opposite order they are created

Y supongo que ya sabes todo eso, pero puedes arreglar tu ejemplo intercambiando las líneas, let mut x ...; y let a ...; .

No estoy seguro de si este problema exacto sería resuelto por alguna de las soluciones propuestas actualmente. Pero espero que veamos pronto, ya que todo esto se está abordando como parte de la hoja de ruta de Rust 2017. here hay un buen lugar para leer las actualizaciones (que también contiene enlaces a las cinco publicaciones de blog relevantes de Niko).