En esta página
Colecciones: Vec, String y HashMap
Colecciones en Rust
Rust proporciona varias colecciones en su biblioteca estándar, todas ubicadas en std::collections. Las tres más importantes son Vec<T>, String y HashMap<K, V>.
Vec: el array dinámico
Vec<T> es la colección más usada en Rust. Es un array de tamaño dinámico que almacena elementos del mismo tipo en el heap:
fn main() {
// Crear un Vec vacío — necesitas especificar el tipo
let mut v1: Vec<i32> = Vec::new();
// Con la macro vec!
let v2 = vec![1, 2, 3, 4, 5];
// Con capacidad inicial (evita realocaciones)
let mut v3: Vec<String> = Vec::with_capacity(10);
// Agregar elementos
v1.push(10);
v1.push(20);
v1.push(30);
// Eliminar y retornar el último
let ultimo = v1.pop(); // Some(30)
println!("{:?}", ultimo);
// Insertar en una posición específica
v1.insert(0, 5); // Inserta 5 al inicio
// Eliminar por índice
let eliminado = v1.remove(0); // Elimina y retorna el elemento en índice 0
println!("Eliminado: {eliminado}");
println!("v1: {:?}", v1);
println!("v2: {:?}", v2);
}Acceso a elementos
fn main() {
let v = vec![10, 20, 30, 40, 50];
// Acceso por índice — puede entrar en pánico si está fuera de rango
let tercero = v[2];
println!("Tercero: {tercero}");
// Acceso seguro con get() — retorna Option<&T>
match v.get(100) {
Some(val) => println!("Valor: {val}"),
None => println!("Índice fuera de rango"),
}
// Slices de Vec
let porcion: &[i32] = &v[1..4]; // [20, 30, 40]
println!("Porción: {:?}", porcion);
// first() y last()
println!("Primero: {:?}", v.first());
println!("Último: {:?}", v.last());
}Iteración sobre Vec
fn main() {
let mut numeros = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Iterar con referencia (no consume el Vec)
for n in &numeros {
print!("{n} ");
}
println!();
// Iterar con referencia mutable
for n in &mut numeros {
*n *= 2;
}
println!("{:?}", numeros);
// Iterar por valor (consume el Vec)
let cuadrados: Vec<i32> = numeros.into_iter().map(|n| n * n).collect();
println!("{:?}", cuadrados);
// Filtrar
let pares: Vec<i32> = cuadrados.iter().filter(|&&n| n % 2 == 0).cloned().collect();
println!("Pares: {:?}", pares);
// enumerate
for (i, val) in pares.iter().enumerate() {
println!("[{i}] = {val}");
}
}Operaciones comunes de Vec
fn main() {
let mut v = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
println!("Longitud: {}", v.len());
println!("¿Vacío? {}", v.is_empty());
println!("¿Contiene 5? {}", v.contains(&5));
// Ordenar
v.sort();
println!("Ordenado: {:?}", v);
// Deduplicar (requiere ordenado previo)
v.dedup();
println!("Sin duplicados: {:?}", v);
// Invertir
v.reverse();
println!("Invertido: {:?}", v);
// Truncar
v.truncate(5);
println!("Truncado a 5: {:?}", v);
// retain: conservar solo los que cumplen la condición
v.retain(|&n| n > 2);
println!("Solo > 2: {:?}", v);
// extend: agregar elementos de otro iterable
v.extend([10, 20, 30]);
println!("Extendido: {:?}", v);
// split_at
let (izquierda, derecha) = v.split_at(3);
println!("Izquierda: {:?}, Derecha: {:?}", izquierda, derecha);
}String: texto UTF-8 poseído
String es un vector de bytes UTF-8 con ownership. Diferente de &str, un String puede crecer y modificarse:
fn main() {
// Crear
let mut s = String::new();
let s2 = String::from("Hola");
let s3 = "mundo".to_string();
// Agregar texto
s.push_str("¡Bienvenido a Rust!");
s.push('!'); // Un solo char
// Concatenación con +
// Nota: s2 se mueve (el operador + toma ownership del primer argumento)
let s4 = s2 + " " + &s3;
println!("{s4}");
// format!: concatenar sin mover
let parte1 = String::from("a");
let parte2 = String::from("b");
let parte3 = String::from("c");
let unida = format!("{parte1}-{parte2}-{parte3}"); // Ninguna se mueve
println!("{unida}");
// Métodos útiles
let texto = String::from(" Hola, Rust! ");
println!("Trim: '{}'", texto.trim());
println!("Mayúsculas: {}", texto.to_uppercase());
println!("Minúsculas: {}", texto.to_lowercase());
println!("Reemplazar: {}", texto.replace("Rust", "Mundo"));
// Dividir
let csv = "uno,dos,tres,cuatro";
let partes: Vec<&str> = csv.split(',').collect();
println!("{:?}", partes);
// Verificar
println!("¿Contiene 'Rust'? {}", texto.contains("Rust"));
println!("¿Empieza con ' Hola'? {}", texto.starts_with(" Hola"));
// Longitud — en bytes, no en caracteres
let emoji = "🦀";
println!("Bytes de '{}': {}", emoji, emoji.len()); // 4
println!("Chars de '{}': {}", emoji, emoji.chars().count()); // 1
}HashMap: el mapa clave-valor
HashMap<K, V> asocia claves de tipo K con valores de tipo V. Las claves deben implementar Eq y Hash:
use std::collections::HashMap;
fn main() {
let mut poblacion: HashMap<&str, u64> = HashMap::new();
poblacion.insert("Argentina", 46_000_000);
poblacion.insert("Bolivia", 12_000_000);
poblacion.insert("Chile", 19_500_000);
poblacion.insert("Colombia", 51_000_000);
// Buscar
println!("{:?}", poblacion.get("Bolivia")); // Some(12000000)
println!("{}", poblacion["Argentina"]); // 46000000 (puede entrar en pánico)
// contains_key
println!("¿Tiene Peru? {}", poblacion.contains_key("Peru"));
// Eliminar
let removido = poblacion.remove("Chile");
println!("Removido: {:?}", removido);
// Iterar (orden no garantizado)
let mut paises: Vec<&&str> = poblacion.keys().collect();
paises.sort();
for pais in paises {
println!("{}: {}", pais, poblacion[pais]);
}
// Longitud
println!("Total países: {}", poblacion.len());
}La entry API
La API entry es la forma idiomática de trabajar con valores que pueden o no existir:
use std::collections::HashMap;
fn contar_palabras(texto: &str) -> HashMap<&str, usize> {
let mut conteo = HashMap::new();
for palabra in texto.split_whitespace() {
// Si la clave no existe, inserta 0; luego incrementa
let count = conteo.entry(palabra).or_insert(0);
*count += 1;
}
conteo
}
fn main() {
let texto = "el gato come el ratón el gato duerme";
let conteo = contar_palabras(texto);
// Ordenar para output determinístico
let mut palabras: Vec<(&&str, &usize)> = conteo.iter().collect();
palabras.sort_by_key(|&(p, _)| *p);
for (palabra, n) in palabras {
println!("{palabra}: {n}");
}
// or_insert_with: calcular el valor por defecto solo si es necesario
let mut cache: HashMap<String, Vec<i32>> = HashMap::new();
cache.entry(String::from("clave")).or_insert_with(Vec::new).push(42);
cache.entry(String::from("clave")).or_insert_with(Vec::new).push(99);
println!("{:?}", cache);
}Crear HashMap desde iteradores
use std::collections::HashMap;
fn main() {
// Desde dos vectores paralelos
let claves = vec!["uno", "dos", "tres"];
let valores = vec![1, 2, 3];
let mapa: HashMap<&str, i32> = claves.into_iter().zip(valores).collect();
println!("{:?}", mapa);
// Desde un Vec de tuplas
let pares = vec![("a", 1), ("b", 2), ("c", 3)];
let mapa2: HashMap<&str, i32> = pares.into_iter().collect();
println!("{:?}", mapa2);
}Con el dominio de las colecciones principales, estás listo para explorar los traits — el sistema de abstracciones de Rust que permite el polimorfismo sin herencia.
use std::collections::HashMap;
fn main() {
// === Vec<T> ===
let mut numeros: Vec<i32> = Vec::new();
numeros.push(1);
numeros.push(2);
numeros.push(3);
// Macro vec! para inicializar
let frutas = vec!["manzana", "banana", "cereza"];
// Iteración
let dobles: Vec<i32> = numeros.iter().map(|n| n * 2).collect();
println!("{:?}", dobles);
// === HashMap ===
let mut scores: HashMap<String, u32> = HashMap::new();
scores.insert(String::from("Ana"), 95);
scores.insert(String::from("Bob"), 87);
// entry API: insertar solo si no existe
scores.entry(String::from("Ana")).or_insert(100);
scores.entry(String::from("Carlos")).or_insert(78);
for (nombre, score) in &scores {
println!("{nombre}: {score}");
}
// Buscar
if let Some(score) = scores.get("Ana") {
println!("Score de Ana: {score}");
}
}
Inicia sesión para guardar tu progreso