Un Vec<T> es una colección de elementos del mismo tipo almacenados de forma contigua en la memoria heap. A diferencia de los arrays estándar de Rust ([T; N]), cuyo tamaño debe conocerse en tiempo de compilación y se guardan en el stack, los Vec pueden crecer o encogerse durante la ejecución.

Para lograr esto, un Vec gestiona tres valores internamente: un puntero a la memoria en el heap, la longitud (len), que es cuántos elementos hay actualmente, y la capacidad (capacity), que es el espacio total reservado. Cuando añades elementos y el Vec se llena, Rust realiza una reasignación: reserva un nuevo bloque de memoria (usualmente duplicando la capacidad actual), copia los elementos antiguos al nuevo bloque y libera la memoria anterior. Esto permite que la inserción al final sea muy eficiente.

Es fundamental distinguir entre el acceso por índice (v[i]) y el método .get(i). El primero causará un panic (el programa se detendrá abruptamente) si intentas acceder a una posición fuera de los límites. El segundo es la forma segura de trabajar, ya que devuelve un Option<&T> (Some(&valor) si existe, o None si no).

Ejemplo principal

fn main() {
    // 1. Creación: macro vec! o Vec::new()
    // Usamos Vec::with_capacity para optimizar si conocemos el tamaño aproximado
    let mut numeros = Vec::with_capacity(5);
    numeros.extend([10, 20, 20, 30]); // Agrega múltiples elementos

    // 2. Modificación
    numeros.push(40);          // Agrega 40 al final
    numeros.insert(1, 15);     // Inserta 15 en el índice 1, desplaza el resto
    let extraido = numeros.remove(2); // Elimina el elemento en el índice 2 y lo retorna
    let ultimo = numeros.pop();      // Elimina el último y retorna Option<i32>

    // 3. Limpieza y filtrado
    numeros.push(20);
    numeros.sort();            // Ordenar es necesario para que dedup funcione correctamente
    numeros.dedup();           // Elimina duplicados consecutivos
    numeros.retain(|&x| x > 15); // Mantiene solo elementos que cumplen la condición

    // 4. Inspección y acceso seguro
    println!("Vector: {:?}", numeros);
    println!("Longitud: {}, Capacidad: {}", numeros.len(), numeros.capacity());
    println!("¿Está vacío?: {}", numeros.is_empty());

    if let Some(val) = numeros.get(0) {
        println!("El primer elemento es: {}", val);
    }

    // 5. Búsqueda y otros
    if numeros.contains(&30) {
        println!("El 30 está en el vector");
    }

    // 6. Limpieza total
    numeros.clear();
    println!("¿Está vacío tras clear()?: {}", numeros.is_empty());
    println!("Extraído con pop: {:?}", extraido);
    println!("Último elemento (pop): {:?}", ultimo);
}

Explicación del Código

  • let mut numeros = Vec::with_capacity(5);: Se crea un vector vacío con capacidad inicial de 5 elementos para evitar reasignaciones inmediatas en el heap.
  • numeros.extend([10, 20, 20, 30]);: El método extend toma un iterador y añade todos sus elementos al vector numeros.
  • numeros.push(40);: El método push añade el valor 40 al final de la colección.
  • numeros.insert(1, 15);: Inserta el valor 15 en la posición de índice 1, moviendo los elementos existentes hacia la derecha.
  • let extraido = numeros.remove(2);: El método remove(2) elimina el elemento en el índice 2 y lo devuelve para que pueda ser usado en la variable extraido.
  • let ultimo = numeros.pop();: pop() elimina el último elemento y devuelve un Option<i32> (Some(valor) si hay elementos, None si está vacío).
  • numeros.sort();: Ordena el vector, lo cual es un requisito previo para que dedup elimine todos los duplicados.
  • numeros.dedup();: Elimina elementos duplicados que estén uno al lado del otro.
  • numeros.retain(|&x| x > 15);: Aplica un predicado; solo los elementos donde x > 15 se mantienen en el vector.
  • numeros.len() y numeros.capacity(): Devuelven la cantidad de elementos actuales y el espacio total reservado, respectivamente.
  • numeros.is_empty(): Retorna un booleano que indica si el vector no tiene elementos.
  • numeros.get(0): Acceso seguro al primer elemento. Devuelve un Option<&i32>.
  • numeros.contains(&30): Comprueba si el valor 30 existe dentro del vector.
  • numeros.clear(): Elimina todos los elementos del vector, dejando su longitud en 0 pero manteniendo la capacidad.

70