Box

Box se trata de un tipo muy util en Rust. Permite almacenar en el heap (el montón) un valor, en lugar de almacenarlo en la pila. Para crear un elemento de este tipo se usa Box::new() con el elemento como parámetro.

fn just_takes_a_variable<T>(item: T) {} // Toma cualquier variable y la olvida.

fn main() {
    let my_number = 1; // Este es de tipo i32
    just_takes_a_variable(my_number);
    just_takes_a_variable(my_number); // no hay problema en usarla dos veces porque el tipo permite Copy

    let my_box = Box::new(1); // Este es de tipo Box<i32>
    just_takes_a_variable(my_box.clone()); // Sin .clone() la segunda función daría error
    just_takes_a_variable(my_box); // debido a que Box no dispone de Copy
}

Al principio, resulta difícil pensar en la utilidad de este tipo, pero se usa mucho en Rust. Si se recuerda, & se usa para los str debido a que el compilador no conoce su tamaño: str puede ser de cualquier longitud. Y la referencia & sí que tiene un tamaño conocido, siempre igual. Box se comporta de forma similar. Además, * sirve para extraer el valor de un Box. Igual que sucede con &:

fn main() {
    let my_box = Box::new(1); // Este es Box<i32>
    let an_integer = *my_box; // Este es i32
    println!("{:?}", my_box);
    println!("{:?}", an_integer);
}

Por este motivo se denomina "puntero inteligente" a Box, porque es como una referencia (un tipo de puntero), pero facilita otro conjunto de cosas.

Se puede usar Box para crear un struct con la misma estructura en su interior. A esto se le denomina estructura recursiva. Es decir, que dentro de un Struc A puede haber otro Struct A. En ocasiones, se puede necesitar Box para crear listas enlazadas, aunque este tipo de listas no son muy populares en Rust. A continuación se muestra un ejemplo que muestra lo que sucede si se intenta crear una estructura recursiva sin Box:

#![allow(unused)]
fn main() {
struct List {
    item: Option<List>, // ⚠️
}
}

Este struct simple contiene un único elemento que puede ser Some<List> o None. Debido a que se puede incorporar este último, el struct no es recursivo hasta el infinito. Pero el compilador no puede conocer su tamaño para poder depositar sus elementos en la pila:

error[E0072]: recursive type `List` has infinite size
 --> src/main.rs:3:1
  |
3 | struct List {
  | ^^^^^^^^^^^ recursive type has infinite size
4 |     item: Option<List>, // ⚠️
  |           ------------ recursive without indirection
  |
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable

Se observa que el propio compilador sugiere que se use Box. Si se aplica este cambio:

struct List {
    item: Option<Box<List>>,
}
fn main() {}

Ahora el compilador acepta el struct List, debido a que la recursividad se encuentra detrás de un Box y el tamaño de este es conocido.

Un lista simple podría definirse así:

struct List {
    item: Option<Box<List>>,
}

impl List {
    fn new() -> List {
        List {
            item: Some(Box::new(List { item: None })),
        }
    }
}

fn main() {
    let mut my_list = List::new();
}

Incluso sin datos es un poco complejo. Rust no usa este tipo de patrón muy a menudo. Esto se debe a las estrictas reglas de propiedad y préstamo que tiene Rust. En todo caso, si se necesita un tipo cualquiera de lista enlazada, Box puede ayudar.

Box permite el uso de std::mem::drop sobre un elemento de este tipo ya que el elemento se encuentra en el heap (montón). Esto puede ser útil en ocasiones.