memory-management rust

memory management - ¿Rust libera la memoria de las variables sobrescritas?



memory-management (2)

Vi en el libro de Rust que puedes definir dos variables diferentes con el mismo nombre:

let hello = "Hello"; let hello = "Goodbye"; println!("My variable hello contains: {}", hello);

Esto imprime:

My variable hello contains: Goodbye

¿Qué pasa con el primer hola? ¿Se libera? ¿Cómo podría acceder?

Sé que sería malo nombrar dos variables de la misma manera, pero si esto sucede por accidente porque lo declaro 100 líneas debajo, podría ser un verdadero dolor.


Hay una diferencia entre sombrear y sobrescribir variables cuando se trata de soltar el orden.

Normalmente, todas las variables locales se descartan cuando salen del alcance, en orden inverso a la declaración (consulte el capítulo El lenguaje de programación de Rust en Drop ). Esto incluye variables sombreadas. Es fácil verificar esto envolviendo el valor en una estructura de envoltura simple que imprime algo cuando se cae (la envoltura) (justo antes de que se caiga el valor en sí):

use std::fmt::Debug; struct NoisyDrop<T: Debug>(T); impl<T: Debug> Drop for NoisyDrop<T> { fn drop(&mut self) { println!("dropping {:?}", self.0); } } fn main() { let hello = NoisyDrop("Hello"); let hello = NoisyDrop("Goodbye"); println!("My variable hello contains: {}", hello.0); }

imprime lo siguiente (área de playground ):

My variable hello contains: Goodbye dropping "Goodbye" dropping "Hello"

Esto se debe a que un nuevo enlace let en un ámbito no sobrescribe el enlace anterior, por lo que es como si hubiera escrito

let hello1 = NoisyDrop("Hello"); let hello2 = NoisyDrop("Goodbye"); println!("My variable hello contains: {}", hello2.0);

Observe que este comportamiento es diferente del siguiente, superficialmente muy similar, código ( playground ):

fn main() { let mut hello = NoisyDrop("Hello"); hello = NoisyDrop("Goodbye"); println!("My variable hello contains: {}", hello.0); }

que no solo los elimina en el orden opuesto, sino que elimina el primer valor antes de imprimir el mensaje. Esto se debe a que cuando asigna a una variable (en lugar de sombrearla con una nueva), el valor original se descarta primero , antes de que se mueva el nuevo valor.

Comencé diciendo que las variables locales son "normalmente" descartadas cuando salen del alcance. Debido a que puede mover valores dentro y fuera de las variables, el análisis de averiguar cuándo las variables deben eliminarse a veces no se puede hacer hasta el tiempo de ejecución. En tales casos, el compilador realmente inserta código para rastrear la "vitalidad" y descartar esos valores cuando sea necesario , por lo que no puede causar fugas accidentalmente al sobrescribir un valor. (Sin embargo, todavía es posible perder memoria de forma segura llamando a mem::forget o creando un ciclo Rc con mutabilidad interna).


Rust no tiene un recolector de basura .

¿Rust libera la memoria de las variables sobrescritas?

Sí, de lo contrario sería una pérdida de memoria, lo que sería una decisión de diseño bastante terrible.

que pasa con el primer hola

Está Shadowing .

No sucede nada "especial" con los datos a los que hace referencia la variable, aparte del hecho de que ya no puede acceder a ellos. Todavía se descarta cuando la variable se sale del alcance.

Aquí hay un código que se imprime cuando se descarta cada variable:

struct Noisy; impl Drop for Noisy { fn drop(&mut self) { println!("Dropped") } } fn main() { println!("0"); let thing = Noisy; println!("1"); let thing = Noisy; println!("2"); }

0 1 2 Dropped Dropped

Sé que sería malo nombrar dos variables iguales

No es "malo", es una decisión de diseño. Yo diría que usar sombreado como este es una mala idea:

let x = "Anna"; println!("User''s name is {}", x); let x = 42; println!("The tax rate is {}", x);

Usar sombreado como este es razonable para mí:

let name = String::from(" Vivian "); let name = name.trim(); println!("User''s name is {}", x);

Ver también:

  • ¿Por qué necesito volver a vincular / sombrear cuando puedo tener un enlace variable mutable?

pero si esto sucede por accidente porque lo declaro 100 líneas debajo, podría ser un verdadero dolor.

No tenga funciones que sean tan grandes que "accidentalmente" haga algo. Eso es aplicable en cualquier lenguaje de programación.

¿Hay alguna forma de limpiar la memoria manualmente?

Puedes llamar a drop :

println!("0"); let thing = Noisy; drop(thing); println!("1"); let thing = Noisy; println!("2");

0 Dropped 1 2 Dropped

Sin embargo, como señala oli_obk - ker , la memoria de la pila tomada por la variable no se liberará hasta que salga la función, solo los recursos tomados por la variable.

Toda discusión sobre la caída requiere mostrar su implementación (muy complicada):

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

¿Qué sucede si declaro la variable en un ámbito global fuera de las otras funciones?

Las variables globales nunca se liberan, incluso si puede crearlas para empezar.