HashMap en Rust
Un HashMap (mapa hash) guarda datos como pares clave-valor: buscas las cosas por una clave, en lugar de por su posición como harías con un array o un vector. Si has usado un diccionario en Python o un objeto como mapa en JavaScript, es la misma idea.
Crear un HashMap
Sección titulada «Crear un HashMap»use std::collections::HashMap;
fn main() { let mut ages = HashMap::new();
ages.insert("Alicia", 30); ages.insert("Bob", 25);
println!("{:?}", ages);}La línea use std::collections::HashMap; del principio trae el tipo al ámbito. A diferencia de Vec y String, HashMap no está disponible por defecto, así que esta línea es obligatoria.
Buscar un valor
Sección titulada «Buscar un valor»use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alicia", 30);
match ages.get("Alicia") { Some(age) => println!("Alicia tiene {age} años"), None => println!("No encontrada"), }}.get() devuelve un Option en lugar del valor directamente, porque puede que la clave no exista. Veremos Option a fondo en su propia página; por ahora, un match con Some/None es la forma habitual de manejarlo.
Actualizar un valor
Sección titulada «Actualizar un valor»Si vuelves a insertar con la misma clave, se sobrescribe el valor anterior:
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alicia", 30); ages.insert("Alicia", 31); // sobrescribe el 30
println!("{:?}", ages.get("Alicia"));}Si solo quieres insertar un valor cuando la clave todavía no existe, usa .entry():
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alicia", 30);
ages.entry("Alicia").or_insert(99); // Alicia ya existe, así que no hace nada ages.entry("Bob").or_insert(25); // Bob no existe, así que lo inserta
println!("{:?}", ages);}Recorrer un HashMap con un bucle
Sección titulada «Recorrer un HashMap con un bucle»use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alicia", 30); ages.insert("Bob", 25);
for (name, age) in &ages { println!("{name} tiene {age} años"); }}Cuándo usar un HashMap
Sección titulada «Cuándo usar un HashMap»Usa un HashMap siempre que quieras buscar cosas por un nombre o un ID en lugar de por su posición: contar cuántas veces aparece cada palabra, relacionar nombres de usuario con sus datos, guardar resultados en caché por clave. Si el orden importa o simplemente estás recorriendo una lista, un Vec encaja mejor.
Práctica
Sección titulada «Práctica»1. Una pequeña agenda telefónica
Sección titulada «1. Una pequeña agenda telefónica»Guarda tres nombres con sus números de teléfono en un HashMap. Después, busca a "Ben" (que sí está) y a "Dana" (que no está), e imprime una línea amable para cada uno. Pista: recorre ["Ben", "Dana"] y haz match sobre .get(name).
Ver solución
use std::collections::HashMap;
fn main() { let mut phone_book = HashMap::new(); phone_book.insert("Ana", "555-1234"); phone_book.insert("Ben", "555-9876"); phone_book.insert("Chen", "555-4321");
for name in ["Ben", "Dana"] { match phone_book.get(name) { Some(number) => println!("{name}: {number}"), None => println!("{name}: no está en la agenda"), } }}Salida:
Ben: 555-9876Dana: no está en la agendaRecorrer los nombres que nos interesan, en lugar del propio mapa, también nos da un orden fijo. Es un truco práctico siempre que necesites una salida predecible de un HashMap.
2. Cuenta los votos
Sección titulada «2. Cuenta los votos»Tienes los votos ["rojo", "azul", "rojo", "verde", "azul", "rojo"]. Cuenta cuántos recibió cada color e imprime los totales de "rojo", "verde", "azul" y "rosa" (que no recibió ninguno). Pista: counts.entry(color).or_insert(0) te da el conteo actual de ese color, empezando en 0. Súmale uno con *count += 1.
Ver solución
use std::collections::HashMap;
fn main() { let votes = ["rojo", "azul", "rojo", "verde", "azul", "rojo"]; let mut counts = HashMap::new();
for color in votes { let count = counts.entry(color).or_insert(0); *count += 1; }
for color in ["rojo", "verde", "azul", "rosa"] { match counts.get(color) { Some(n) => println!("{color}: {n}"), None => println!("{color}: 0"), } }}Salida:
rojo: 3verde: 1azul: 2rosa: 0or_insert(0) devuelve una referencia mutable al valor guardado en el mapa, no una copia. El * de *count += 1 significa “cambia el número al que apunta”, así que el propio mapa se actualiza. Este patrón de “entry, o inserta 0, y luego suma 1” es la forma habitual de contar cosas en Rust.
Resumen
Sección titulada «Resumen»HashMap<K, V>guarda pares clave-valor; tráelo al ámbito conuse std::collections::HashMap;..insert(clave, valor)añade o sobrescribe;.get(clave)busca y devuelve unOption..entry(clave).or_insert(valor)inserta solo si la clave todavía no está.- El orden al recorrerlo no está garantizado: no dependas de él.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. A diferencia de Vec o String, ¿qué necesitas para traer HashMap al ámbito?
2. ¿Qué hace .entry(key).or_insert(value)?
3. ¿Puedes confiar en el orden en que se imprimen las entradas al recorrer un HashMap?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- Guardar claves con valores asociados en hash maps — El libro de Rust (en inglés): explica el ownership de claves y valores, con un ejemplo que cuenta palabras actualizando valores.
- HashMap — Rust by Example (en inglés): un ejemplo ejecutable de contactos que usa
insert,get,removey bucles. HashMap— documentación de la biblioteca estándar (en inglés): todos los métodos, incluidoscontains_key,remove,keysyvalues.Entry— documentación de la biblioteca estándar (en inglés): todo lo que puedes hacer con.entry(), comoor_insert_withyand_modify.