rust temporary

rust - ¿Por qué es legal pedir prestado un temporal?



temporary (3)

Viniendo de C ++, estoy bastante sorprendido de que este código sea válido en Rust:

let x = &mut String::new(); x.push_str("Hello!");

En C ++, no puede tomar la dirección de un temporal, y un temporal no sobrevivirá a la expresión en la que aparece.

¿Cuánto tiempo vive el temporal en Rust? Y dado que x es solo un préstamo, ¿quién es el propietario de la cadena?


¿Por qué es legal pedir prestado un temporal?

Es legal por la misma razón por la que es ilegal en C ++, porque alguien dijo que así debería ser .

¿Cuánto tiempo vive el temporal en Rust? Y dado que x es solo un préstamo, ¿quién es el propietario de la cadena?

La referencia dice :

La vida útil de los valores temporales es típicamente

  • la declaración que encierra más íntimamente; la expresión de cola de un bloque se considera parte de la declaración que encierra el bloque, o
  • la expresión de condición o la expresión condicional de bucle si el temporal se crea en la expresión de condición de un if o un if / else o en la expresión condicional de loop de una expresión while.

Sin embargo, cuando se crea un valor temporal que se asigna a una declaración let , el temporal se crea con la vida útil del bloque de cierre, ya que el uso de la declaración adjunta (la declaración let ) sería un error garantizado (ya que un puntero a el temporal se almacenaría en una variable, pero el temporal se liberaría antes de que se pudiera usar la variable). El compilador utiliza reglas sintácticas simples para decidir qué valores se asignan a un enlace let y, por lo tanto, merecen una vida útil temporal más larga.

Esencialmente, puede tratar su código como:

let mut a_variable_you_cant_see = String::new(); let x = &mut a_variable_you_cant_see; x.push_str("Hello!");

Ver también:

  • ¿Cuál es el alcance de los valores sin nombre?
  • ¿Están bien los punteros en bruto a los temporales en Rust?

De la referencia de óxido :

Vidas temporales

Cuando se usa una expresión de valor en la mayoría de los contextos de expresión de lugar, se crea una ubicación de memoria temporal sin nombre inicializada para ese valor y la expresión se evalúa en esa ubicación.

Esto aplica, porque String::new() es una expresión de valor y está justo debajo de &mut en un contexto de expresión de lugar. Ahora el operador de referencia solo tiene que pasar por esta ubicación de memoria temporal, por lo que se convierte en el valor de todo el lado derecho (incluido el &mut ).

Sin embargo, cuando se crea una expresión de valor temporal que se asigna a una declaración let, la temporal se crea con la vida útil del bloque que lo encierra.

Dado que está asignado a la variable, obtiene una vida útil hasta el final del bloque de cierre.

Esto también responde a esta pregunta sobre la diferencia entre

let a = &String::from("abcdefg"); // ok!

y

let a = String::from("abcdefg").as_str(); // compile error

En la segunda variante, el temporal se pasa a as_str() , por lo que su vida útil finaliza al final de la instrucción.


El MIR de Rust proporciona una idea de la naturaleza de los temporales; considere el siguiente caso simplificado:

fn main() { let foo = &String::new(); }

y el MIR que produce (comentarios estándar reemplazados por los míos):

fn main() -> () { let mut _0: (); scope 1 { let _1: &std::string::String; // the reference is declared } scope 2 { } let mut _2: std::string::String; // the owner is declared bb0: { StorageLive(_1); // the reference becomes applicable StorageLive(_2); // the owner becomes applicable _2 = const std::string::String::new() -> bb1; // the owner gets a value; go to basic block 1 } bb1: { _1 = &_2; // the reference now points to the owner _0 = (); StorageDead(_1); // the reference is no longer applicable drop(_2) -> bb2; // the owner''s value is dropped; go to basic block 2 } bb2: { StorageDead(_2); // the owner is no longer applicable return; } }

Puede ver que un propietario "invisible" recibe un valor antes de asignarle una referencia y que la referencia se descarta antes que el propietario, como se esperaba.

De lo que no estoy seguro es por qué existe un scope 2 aparentemente inútil y por qué el propietario no se coloca dentro de ningún alcance; Sospecho que MIR todavía no está 100% listo.