¿Qué tiene Rust en lugar de un recolector de basura?

95

Entiendo que Rust no tiene un recolector de basura y me pregunto cómo se libera la memoria cuando un enlace sale de su alcance.

Entonces, en este ejemplo, entiendo que Rust recupera la memoria asignada a 'a' cuando sale del alcance.

{
    let a = 4
}

El problema que tengo con esto es, en primer lugar, cómo sucede esto y, en segundo lugar, ¿no es una especie de recolección de basura? ¿En qué se diferencia de la recolección de basura "típica"?

rix
fuente
12
"Duración de los objetos deterministas". Similar a C ++.
user2864740
@ user2864740 Esa guía está muy desactualizada. El reemplazo moderno probablemente sería doc.rust-lang.org/book/references-and-borrowing.html .
Veedrac

Respuestas:

74

La recolección de basura se usa normalmente de forma periódica o bajo demanda, como si el montón está casi lleno o por encima de algún umbral. Luego busca las variables no utilizadas y libera su memoria, según el algoritmo .

Rust sabría cuándo la variable sale del alcance o su vida útil termina en el tiempo de compilación y, por lo tanto, inserta las instrucciones LLVM / ensamblado correspondientes para liberar la memoria.

Rust también permite algún tipo de recolección de basura, como el conteo de referencias atómicas .

Ayonix
fuente
¿Asignando memoria al introducir variables y liberando memoria cuando ya no se necesita? Realmente no sé qué quieres decir con eso. Tal vez tengamos diferentes opiniones sobre lo que es un GC entonces.
Ayonix
1
Su pregunta es en qué se diferencia el enfoque de Rust de un GC típico. Entonces expliqué qué es un GC y cómo lo hace Rust sin un GC.
Ayonix
1
doc.rust-lang.org/book/the-stack-and-the-heap.html lo explica bastante bien. Sí, hay muchas cosas en la pila, pero mucho menos no es un indicador suficiente (ver Cuadro). Dejé eso por motivos de simplicidad, ya que la pregunta era en general
Ayonix
1
@Amomum En realidad, Rust no tiene ninguna new()función ungida como C, son solo funciones estáticas y, en particular, algo como let x = MyStruct::new()crea su objeto en la pila. El indicador real de asignación de montones es Box::new()(o cualquiera de las estructuras que dependen de Box).
Mario Carneiro
1
¿Qué otros lenguajes manejan la gestión de memoria de forma similar a Rust?
still_dreaming_1
43

La idea básica de administrar recursos (incluida la memoria) en un programa, cualquiera que sea la estrategia, es que los recursos vinculados a "objetos" inalcanzables se pueden recuperar. Más allá de la memoria, esos recursos pueden ser bloqueos mutex, identificadores de archivos, sockets, conexiones de bases de datos ...

Los lenguajes con un recolector de basura escanean periódicamente la memoria (de una forma u otra) para encontrar objetos no utilizados, liberar los recursos asociados con ellos y finalmente liberar la memoria utilizada por esos objetos.

Rust no tiene GC, ¿cómo se las arregla?

El óxido tiene la propiedad. Usando un sistema de tipos afines , rastrea qué variable todavía se aferra a un objeto y, cuando dicha variable sale del alcance, llama a su destructor. Puede ver el sistema de tipos afines en efecto con bastante facilidad:

fn main() {
    let s: String = "Hello, World!".into();
    let t = s;
    println!("{}", s);
}

Rendimientos:

<anon>:4:24: 4:25 error: use of moved value: `s` [E0382]
<anon>:4         println!("{}", s);

<anon>:3:13: 3:14 note: `s` moved here because it has type `collections::string::String`, which is moved by default
<anon>:3         let t = s;
                     ^

lo que ilustra perfectamente que en cualquier momento, a nivel del idioma, se realiza un seguimiento de la propiedad.

Esta propiedad funciona de forma recursiva: si tiene una Vec<String>(es decir, una matriz dinámica de cadenas), entonces cada una Stringes propiedad de la Vecque a su vez es propiedad de una variable u otro objeto, etc. de forma recursiva libera todos los recursos que tenía, incluso indirectamente. En el caso de Vec<String>esto significa:

  1. Liberar el búfer de memoria asociado a cada String
  2. Liberar el búfer de memoria asociado al Vecmismo

Por lo tanto, gracias al seguimiento de la propiedad, la vida útil de TODOS los objetos del programa está estrictamente vinculada a una (o varias) variables de función, que finalmente saldrán del alcance (cuando finalice el bloque al que pertenecen).

Nota: esto es un poco optimista, utilizando el recuento de referencias ( Rco Arc) es posible formar ciclos de referencias y, por lo tanto, causar pérdidas de memoria, en cuyo caso los recursos vinculados al ciclo podrían no liberarse nunca.

Matthieu M.
fuente
2
"Idiomas con Garbage Collector escanean periódicamente la memoria (de una forma u otra)". Muchos lo hacen, pero eso no es cierto en general. Los recolectores de basura en tiempo real escanean de forma incremental en lugar de periódicamente. Los lenguajes de conteo de referencias como Mathematica no escanean en absoluto.
JD
@JonHarrop: No cuento el recuento de referencias como un mecanismo completo de recolección de basura, ya que debe complementarse para evitar ciclos de fugas. En cuanto a la diferencia incremental / periódica, puede que sea mi pobre dominio del inglés, pero no veo cómo periódico no cubre el caso incremental ... Creo que el bit "(de una forma u otra)" transmite adecuadamente que muchos variados existen enfoques. En cualquier caso, si tiene una mejor manera de describir sucintamente la recolección de basura, sugiéralo. Sin embargo, no tengo ninguna intención de lanzarme a una explicación completa: no estoy calificado para ello.
Matthieu M.
1
"No considero el recuento de referencias como un mecanismo completo de recolección de basura, ya que debe complementarse para evitar ciclos de fugas". La RC se considera convencionalmente como una forma de GC. En Mathematica y Erlang, por ejemplo, los ciclos no se pueden crear por diseño, por lo que RC no tiene fugas. Para obtener una perspectiva de alto nivel, consulte "Una teoría unificada de la recolección de basura" cs.virginia.edu/~cs415/reading/bacon-garbage.pdf
JD
@JonHarrop: Verdadero, si no es posible ningún ciclo, RC no puede tener fugas.
Matthieu M.
2
"No veo cómo el periódico no cubre el caso incremental". Los algoritmos de Stop the World se considerarían periódicos, mientras que el marcado tricolor se considera incremental, por ejemplo. Son opuestos en este contexto.
JD
6

Con un lenguaje en el que debe administrar la memoria manualmente, la distinción entre la pila y el montón se vuelve crítica. Cada vez que llama a una función, se asigna suficiente espacio en la pila para todas las variables contenidas dentro del alcance de esa función. Cuando la función regresa, el marco de pila asociado con esa función se "saca" de la pila y la memoria se libera para uso futuro.

Desde un punto de vista práctico, esta limpieza de memoria inadvertida se utiliza como un medio de almacenamiento automático de memoria que se borrará al final del alcance de la función.

Hay más información disponible aquí: https://doc.rust-lang.org/book/the-stack-and-the-heap.html

suizo
fuente
3
Si bien el uso de la pila es útil, la vida útil de los objetos deterministas aún se puede manejar si todos los valores se 'crearon en el montón'. Por tanto, es un detalle de implementación; no necesariamente una estrategia de lenguaje.
user2864740
2
Usted sigue usando esa palabra. No creo que signifique lo que tú crees que significa.
Suiza
Significa lo que deseo expresar ; siendo lo opuesto a las vidas no deterministas. Haz una oferta por una mejor frase.
user2864740
Gracias por la respuesta, le di los puntos al primero simplemente porque se envió primero. La información es igualmente útil y válida.
rix
@ user2864740 La vida útil de los objetos deterministas se refiere a poder decir exactamente cuándo se borrará la memoria del objeto una vez que se haya llamado a su destructor. No tiene nada que ver con cómo se llama a ese destructor en primer lugar. Sigues mencionando el mismo término repetidamente aunque no tiene un significado directo para la pregunta.
Suiza