Lecciones del módulo (2/2)
Smart Pointers: Box y Rc
Un smart pointer (puntero inteligente) es una estructura de datos que se comporta como un puntero, pero posee metadatos y funcionalidades adicionales (como el recuento de referencias o la gestión automática de la memoria).
En Rust, las referencias ordinarias (como &) solo toman prestados datos. Los smart pointers, por el contrario, a menudo poseen los datos a los que apuntan. Eemplos comunes incluyen String y Vec<T>, pero la biblioteca estándar proporciona otros especializados.
Box<T> para asignar en el Heap
El smart pointer más simple es Box<T>, que almacena los datos en el heap en lugar de en la pila. En la pila solo queda el puntero al dato asignado en el heap.
Se usa principalmente en los siguientes casos:
- Cuando se tiene un tipo cuya dimensión no puede ser conocida en el momento de la compilación (como un tipo recursivo), y se quiere utilizar un valor de ese tipo en un contexto que requiere una dimensión exacta.
- Cuando se quieren transferir datos de gran tamaño sin copiarlos.
Ejemplo básico:
fn main() {
let b = Box::new(5); // alloca il valore 5 nello heap
println!("b = {}", *b); // de-referenzia con * per leggere il valore
}
Tipos Recursivos y Cons List
Un tipo recursivo es un tipo que puede contener un valor de sí mismo en su interior. Rust debe conocer la dimensión exacta de un tipo en tiempo de compilación. Dado que la profundidad de un tipo recursivo podría ser infinita, el compilador genera un error a menos que se utilice un Box (que tiene una dimensión fija, ya que es un puntero):
enum List {
Cons(i32, Box<List>),
Nil,
}
Rc<T>: Recuento de Referencias (Reference Counting)
Hay casos en los que un solo valor puede tener múltiples propietarios. El smart pointer Rc<T> (Reference Counted) realiza un seguimiento del número de referencias a un valor para determinar si el valor sigue en uso. Si el recuento llega a cero, el recurso se desasigna de forma segura.
[!NOTE]
Rc<T>se puede utilizar exclusivamente en escenarios de un solo hilo (single-thread). Para escenarios de múltiples hilos (concurrentes) se debe utilizarArc<T>(Atomic Reference Counted).
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = Rc::clone(&a); // incrementa il contatore a 2
println!("Riferimenti: {}", Rc::strong_count(&a));
}
Pruébalo tú
Ejercicio 1: Asignar con Box
Asigna un valor entero igual a 42 en el heap utilizando Box::new y asígnalo a una variable llamada val. Luego, imprime en pantalla el valor almacenado en el Box desreferenciándolo explícitamente con el operador *.
Mostrar pista
Usa `let val = Box::new(42);` para almacenar el valor en el heap y desreferéncialo con `*val` dentro de `println!`.
Solución disponible después de 3 intentos
Ejercicio 2: Estructura de datos recursiva (Cons List)
Define un enum recursivo llamado List que contenga dos variantes: Cons, que encierra una tupla (i32, Box<List>), y Nil, que representa el final de la lista. En el main, instancia una lista enlazada que contenga el elemento 1 seguido de Nil.
Mostrar pista
El enum se declara como `enum List { Cons(i32, Box<List>), Nil }`. Instancia la lista con `List::Cons(1, Box::new(List::Nil))`.
Solución disponible después de 3 intentos
Ejercicio 3: Compartir la propiedad con Rc
Usa std::rc::Rc para crear un entero compartido que contenga el valor 100 dentro de una variable a. Clona la referencia en una variable b usando Rc::clone. Por último, imprime en pantalla el contador de referencias activas usando la función Rc::strong_count.
Mostrar pista
Crea el valor con `Rc::new(100)`. Clona con `Rc::clone(&a)`y muestra el recuento de propietarios con`Rc::strong_count(&a)`.
Solución disponible después de 3 intentos