Y siguiendo con Box, los tipos de datos recursivos o tipos de datos con tamaños dinámicos necesitan usar un Box:
#[derive(Debug)]
enum List<T> {
Cons(T, Box<List<T>>),
Nil,
}
fn main() {
let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
println!("{list:?}");
}
Si no se usaba Box e intentábamos incrustar una Lista directamente en la Lista, el compilador no calcularía un tamaño fijo de la estructura en la memoria (la Lista sería de tamaño infinito).
Box resuelve este problema ya que tiene el mismo tamaño que un puntero normal y solo apunta al siguiente elemento de la Lista.
Veamos que pasa si lo eliminamos:
enum List<T> {
Cons(T, List<T>),
Nil,
}
fn main() {
let list: List<i32> = List::Cons(1, List::Cons(2, List::Nil));
println!("{list:?}");
}
Y si ejecutamos esto:
cargo run
Compiling hello_cargo v0.1.0
error[E0072]: recursive type `List` has infinite size
--> src/main.rs:2:1
|
2 | enum List<T> {
| ^^^^^^^^^^^^
3 | Cons(T, List<T>),
| ------- recursive without indirection
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
|
3 | Cons(T, Box<List<T>>),
| ++++ +
For more information about this error, try `rustc --explain E0072`.
error: could not compile `hello_cargo` due to previous error
Un Box no puede estar vacío, por lo que el puntero siempre es válido y no nulo. Esto permite al compilador optimizar el diseño de la memoria.