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 elVec<Producto>basándose en el campoprecio. Posteriormente,sort_unstable_by(|a, b| b.precio.cmp(&a.precio))realiza un ordenamiento descendente; al serunstable, si dos productos tienen el mismoprecio, su orden relativo original no se garantiza. - Búsqueda: El método
binary_search_by_key(&150, |p| p.precio)busca el valor150comparándolo contra el campopreciode cadaProducto. El resultado será unResult<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 deproductos.productos.split_off(2)extrae todos los elementos desde el índice 2 hasta el final, moviéndolos a una nueva variablesegunda_partey dejando aproductossolo con los elementos originales de los índices 0 y 1.
- Strings:
etiquetas.join("-")es un método optimizado para tipos que implementanBorrow<str>, concatenando los elementos deetiquetascon el separador especificado.
N° 71