La pila, la memoria dinámica y los punteros

La pila ("stack" en inglés), la memoria dinámica ("heap" en inglés) y los punteros son elementos muy importantes en Rust.

La pila y la memoria dinámica son dos tipos de almacenamiento de los datos de un programa durante su ejecución. Sus diferencias más importantes son:

  • La pila es muy rápida, la memoria dinámica no lo es tanto. Tampoco es lenta, pero siempre es más rápido acceder a la pila. Aunque no es posible utilizar la pila siempre porque:
  • Rust necesita conocer el tamaño de una variable en durante su compilación para poder guardarla en la pila. Así, las variables simples como i32 van a la pila ya que se conoce su tamaño exacto. Se sabe que va a ocupar 4 bytes, 32 bits = 4 bytes. Por lo tanto, los datos de tipo i32 pueden ir siempre a la pila.
  • Algunos tipos no tienen un tamaño conocido en tiempo de compilación. No pueden guardarse en la pila. ¿Qué se puede hacer? En primer lugar, se pone la información en la memoria dinámica ya que esta puede contener datos de cualquier tamaño. En segundo lugar, se guarda un puntero en la pila. El tamaño de los punteros es conocid. Así, para recuperar un valor de una variable que está en la memoria dinámica, el ordenador va primero a la pila, obtiene el puntero y lo sigue hasta la memoria dinámica para localizr el dato que se busca.

Los punteros parecen complicados, pero no lo son. Son como una tabla de contenidos de un libro. Imagina este libro:

MI LIBRO

TABLA DE CONTENIDO

Capítulo                        Página
Capítulo 1: mi vida              1
Capítulo 2: mi gato              15
Capítulo 3: mi trabajo           23
Capítulo 4: mi familia           30
Capítulo 5: mis planes futuros   43

La tabla de contenido es como una "pila" que, en este caso, contiene cinco punteros. Puedes leerlos y encontrar la información sobre la que tratan. ¿Dónde está el capítulo sobre "mi vida"? Está en la página 1 (Apunta a la página 1). ¿Dónde está el capítulo sobre "mi trabajo"? Está en la página 23.

El puntero que se ve habitualmente en Rust se denomina referencia. Esto es lo importante que se debe saber: una referencia apunta a la memoria de otro valor. Una referencia supone que se tome prestado el valor, pero no se apropia de él. Es lo mismo que en el libro anterior: la tabla de contenidos no posee la información. Se encuentra en los capítulos que son los que la poseen. En Rust, las referencias llevan el símbolo & al principio de ellas. Así.

  • let mi_variable = 8 crea una variable normal, pero
  • let mi_referencia = &mi_variable crea una referencia. Se lee como "mi_referencia es una referencia a mi_variable" o como "mi_referencia se refiere a mi_variable".

Esto significa que mi_referencia solo mira a los datos de mi_variable. mi_variable sigue siendo propietaria de sus datos. También es posible tener una referencia que "apunte" a otra referencia. Hasta culquier número de referencias.

fn main() {
    let mi_numero = 15; // Esto es un i32
    let referencia_simple = &mi_numero; //  Esto es una &i32
    let referencia_doble = &referencia_simple; // Esto es una &&i32
    let referencia_quintuple = &&&&&mi_numero; // Esto es una &&&&&i32
}

Todos estos son tipos de dato diferentes, de la misma forma que "un amigo de un amigo" es diferente de "un amigo".