En el lenguaje Rust, la gestión de memoria tradicionalmente se ha considerado uno de los problemas más complejos en la programación de bajo nivel. Antes de la aparición de Rust, muchos lenguajes requerían gestión manual de memoria (como C/C++), lo que llevaba a fugas y corrupción de datos. Rust aborda el problema desde otro ángulo: colecciones como Vec<T> utilizan una estrategia de gestión de memoria automática y segura, controlando los momentos de asignación, redistribución (cambio de tamaño) y liberación de memoria a través de un sistema de propiedad y préstamos.
El problema radicaba en que muchos lenguajes o bien abstraen demasiado los detalles del asignador (GC), o hacen que el programador sea responsable de todo (malloc/free). En el caso de los arreglos dinámicos, es crucial estar atento a las fugas y la superación de los límites del arreglo, así como a no violar la propiedad.
La solución en Rust es la automatización a través de abstracciones seguras. Vec<T> asigna memoria en el montón, aumenta su tamaño dinámicamente (generalmente con crecimiento exponencial), y libera todo al salir del ámbito (RAII).
Ejemplo de código:
fn main() { let mut v: Vec<i32> = Vec::new(); v.push(1); v.push(2); v.push(3); // La adición provoca un aumento de tamaño y redistribución de memoria println!("Vector: {:?}", v); // Al salir de main, la memoria se libera automáticamente }
Características clave:
Vec<T> asigna memoria anticipadamente y la redistribuye según sea necesario¿Cuál es la complejidad del crecimiento del arreglo al añadir elementos a Vec?
Generalmente, la complejidad de push es amortiguada O(1), sin embargo, cuando el arreglo se desborda, se asigna una nueva área de memoria (aproximadamente se duplica el tamaño), y todos los elementos son copiados. Este momento es la única excepción en la que la operación se convierte en O(n).
¿Qué sucederá si se intenta obtener un elemento fuera del rango a través de v[index]?
El uso de corchetes lleva a un pánico al sobrepasar los límites. Se debe utilizar el método .get(), que devuelve Option y permite manejar el error de manera segura.
let element = v.get(10); // None, si no hay índice
¿Se puede usar una referencia a un elemento de Vec después de un posible aumento (resize) del vector?
No, después de cambiar el tamaño del vector (por ejemplo, mediante push al desbordar) toda la memoria puede ser movida, y las referencias antiguas se vuelven inválidas, lo que provoca un error de compilación (o comportamiento indefinido en un bloque unsafe, si se usan manualmente).
Un desarrollador implementa un caché de mensajes basado en Vec<T> y entrega referencias a elementos. Después de una nueva inserción, ocurre una redistribución de memoria, y todas las referencias existentes se vuelven "colgantes". Y el resultado es un fallo de la aplicación.
Ventajas:
Desventajas:
Se utiliza ya sea identificación interna de elementos (índices/claves + comprobación de validez), o se devuelven solo copias/valores inmutables, evitando almacenar referencias de larga duración a elementos Vec.
Ventajas:
Desventajas: