El tipo Vec<T> en Rust es una estructura de datos extremadamente versátil que permite no solo almacenar elementos, sino también organizarlos, buscarlos eficientemente y crear vistas sobre sus datos sin necesidad de duplicarlos de forma inmediata.

Para el ordenamiento, Rust ofrece tres enfoques principales. sort() es un algoritmo estable (mantiene el orden relativo de elementos iguales) y utiliza Timsort. sort_unstable() es generalmente más rápido porque no garantiza la estabilidad, lo cual es ideal cuando el orden de elementos con la misma clave no importa. Para personalización, sort_by(|a, b| ...) permite definir una lógica de comparación arbitraria mediante una función que devuelva Ordering, mientras que sort_by_key(|x| ...) es una forma más sintáctica de ordenar basándose en una transformación de un campo específico.

La búsqueda eficiente se logra mediante binary_search, la cual requiere que el Vec esté previamente ordenado. Este método es fundamental para optimizar búsquedas en grandes volúmenes de datos ($O(\log n)$). Un detalle crucial es su retorno de tipo Result<usize, usize>: si el elemento existe, devuelve Ok(índice), si no, devuelve Err(índice), donde el índice es la posición donde se debería insertar el elemento para mantener el orden.

En cuanto a la inspección de datos, las “vistas” permiten iterar sobre el contenido de formas distintas: windows(n) proporciona iteradores sobre sub-slices solapados (útil para algoritmos de media móvil), mientras que chunks(n) divide el vector en partes contiguas no solapadas. chunks_exact(n) es una variante más estricta que garantiza que cada sub-slice tenga exactamente el tamaño n, permitiendo acceder al sobrante mediante el método remainder().

Finalmente, para la manipulación estructural, drain(rango) elimina un rango de elementos devolviendo un iterador con ellos, permitiendo una extracción eficiente. split_off(i) divide el vector original en dos, moviendo los elementos desde el índice i hasta el final al nuevo vector. splice(rango, iter) es una herramienta potente para reemplazar un rango de elementos por cualquier otro iterador.

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)]
struct Producto {
    id: u32,
    precio: u32,
}

fn main() {
    // 1. Preparación de datos
    let mut productos = vec![
        Producto { id: 1, precio: 100 },
        Producto { id: 2, precio: 50 },
        Producto { id: 3, precio: 200 },
        Producto { id: 4, precio: 50 }, // Duplicado de precio para probar estabilidad
        Producto { id: 5, precio: 150 },
    ];

    // 2. Ordenamiento
    // sort_by_key: Ordena por precio de menor a mayor
    productos.sort_by_key(|p| p.precio);
    
    // sort_unstable_by: Ordenar de mayor a menor (usando cmp invertido)
    // No preserva el orden de productos con el mismo precio
    productos.sort_unstable_by(|a, b| b.precio.cmp(&a.precio));

    // 3. Búsqueda binaria (requiere ordenación previa)
    // Buscamos un producto con precio 150
    // Nota: Usamos binary_search_by_key para comparar el campo 'precio'
    let resultado_busqueda = productos.binary_search_by_key(&150, |p| p.precio);
    
    // 4. Vistas (Windows y Chunks)
    // windows(2) -> [ [150, 100], [100, 200] ... ] (solapados)
    let pares_solapados: Vec<_> = productos.windows(2).collect();

    // chunks(2) -> [ [150, 100], [200, 50] ... ] (no solapados)
    let grupos_no_solapados: Vec<_> = productos.chunks(2).collect();

    // 5. Manipulación de estructura
    // drain(1..3) extrae elementos en el rango [1, 3)
    let extraidos: Vec<_> = productos.drain(1..3).collect();

    // split_off(2) divide el vector en dos en el índice 2
    let mut segunda_parte = productos.split_off(2);

    // 6. Concatenación de Strings
    let etiquetas = vec!["Prod_A", "Prod_B", "Prod_C"];
    let etiqueta_unida = etiquetas.join("-"); // "Prod_A-Prod_B-Prod_C"
}

Explicación del Código

  • Ordenamiento: Se utiliza sort_by_key(|p| p.precio) para organizar el Vec<Producto> basándose en el campo precio. Posteriormente, sort_unstable_by(|a, b| b.precio.cmp(&a.precio)) realiza un ordenamiento descendente; al ser unstable, si dos productos tienen el mismo precio, su orden relativo original no se garantiza.
  • Búsqueda: El método binary_search_by_key(&150, |p| p.precio) busca el valor 150 comparándolo contra el campo precio de cada Producto. El resultado será un Result<usize, usize>.
  • Vistas:
    • windows(2) genera sub-slices de longitud 2 que se solapan: el segundo sub-slice comienza en el segundo elemento del vector original.
    • chunks(2) genera sub-slices de longitud 2 que no se solapan: el segundo sub-slice comienza después de que el primero ha terminado.
  • Manipulación:
    • productos.drain(1..3) elimina los elementos en los índices 1 y 2 del vector original y los devuelve en un iterador, reduciendo el tamaño de productos.
    • productos.split_off(2) extrae todos los elementos desde el índice 2 hasta el final, moviéndolos a una nueva variable segunda_parte y dejando a productos solo con los elementos originales de los índices 0 y 1.
  • Strings: etiquetas.join("-") es un método optimizado para tipos que implementan Borrow<str>, concatenando los elementos de etiquetas con el separador especificado.

71