HashMap em Rust
Um HashMap guarda dados como pares de chave e valor. Você busca as coisas por uma chave, e não pela posição, como faria com um array ou um vetor. Se você já usou um dicionário em Python ou um objeto como mapa em JavaScript, é a mesma ideia.
Criando um HashMap
Seção intitulada “Criando um HashMap”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new();
ages.insert("Alice", 30); ages.insert("Bob", 25);
println!("{:?}", ages);}O use std::collections::HashMap; no topo traz o tipo para o escopo. Diferente de Vec e String, o HashMap não vem disponível por padrão, então essa linha é obrigatória.
Buscando um valor
Seção intitulada “Buscando um valor”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30);
match ages.get("Alice") { Some(age) => println!("Alice tem {age} anos"), None => println!("Não encontrado"), }}.get() devolve um Option em vez do valor direto, porque a chave pode não existir. Vamos ver o Option com calma na página dele. Por enquanto, um match com Some/None é o jeito padrão de lidar com isso.
Atualizando um valor
Seção intitulada “Atualizando um valor”Inserir de novo com a mesma chave substitui o valor antigo:
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30); ages.insert("Alice", 31); // substitui o 30
println!("{:?}", ages.get("Alice"));}Se você só quer inserir um valor quando a chave ainda não existe, use .entry():
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30);
ages.entry("Alice").or_insert(99); // Alice já existe, então nada acontece ages.entry("Bob").or_insert(25); // Bob não existe, então é inserido
println!("{:?}", ages);}Percorrendo um HashMap
Seção intitulada “Percorrendo um HashMap”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30); ages.insert("Bob", 25);
for (name, age) in &ages { println!("{name} tem {age} anos"); }}Quando usar um HashMap
Seção intitulada “Quando usar um HashMap”Use um HashMap sempre que quiser buscar coisas por um nome ou ID em vez de uma posição: contar quantas vezes cada palavra aparece, ligar nomes de usuário aos dados de cada usuário, guardar resultados em cache por chave. Se a ordem importa ou você só está trabalhando com uma lista simples, um Vec é a escolha melhor.
Prática
Seção intitulada “Prática”1. Uma agenda telefônica pequena
Seção intitulada “1. Uma agenda telefônica pequena”Guarde três nomes e telefones em um HashMap. Depois, procure "Ben" (que está lá) e "Dana" (que não está) e imprima uma linha amigável para cada um. Dica: percorra ["Ben", "Dana"] e faça um match em .get(name).
Ver solução
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}: não está na agenda"), } }}Saída:
Ben: 555-9876Dana: não está na agendaPercorrer os nomes que queremos, em vez do próprio mapa, também nos dá uma ordem fixa. É um truque útil sempre que você precisa de uma saída previsível a partir de um HashMap.
2. Conte os votos
Seção intitulada “2. Conte os votos”Você tem os votos ["vermelho", "azul", "vermelho", "verde", "azul", "vermelho"]. Conte quantos votos cada cor recebeu e imprima os totais de "vermelho", "verde", "azul" e "rosa" (que não recebeu nenhum). Dica: counts.entry(color).or_insert(0) dá a contagem atual daquela cor, começando em 0. Some um a ela com *count += 1.
Ver solução
use std::collections::HashMap;
fn main() { let votes = ["vermelho", "azul", "vermelho", "verde", "azul", "vermelho"]; let mut counts = HashMap::new();
for color in votes { let count = counts.entry(color).or_insert(0); *count += 1; }
for color in ["vermelho", "verde", "azul", "rosa"] { match counts.get(color) { Some(n) => println!("{color}: {n}"), None => println!("{color}: 0"), } }}Saída:
vermelho: 3verde: 1azul: 2rosa: 0O or_insert(0) devolve uma referência mutável para o valor guardado no mapa, e não uma cópia. O * em *count += 1 quer dizer “altere o número para o qual isto aponta”, então o próprio mapa é atualizado. Esse padrão de “entry, ou insira 0, depois some 1” é o jeito padrão de contar coisas em Rust.
HashMap<K, V>guarda pares de chave e valor; traga para o escopo comuse std::collections::HashMap;..insert(key, value)adiciona ou substitui;.get(key)busca e devolve umOption..entry(key).or_insert(value)só insere se a chave ainda não estiver lá.- A ordem ao percorrer não é garantida. Não conte com ela.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. O que você precisa para trazer o HashMap para o escopo, ao contrário de Vec ou String?
2. O que .entry(key).or_insert(value) faz?
3. Dá para confiar na ordem em que as entradas aparecem ao percorrer um HashMap?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- Storing Keys with Associated Values in Hash Maps — The Rust Book (em inglês): explica o ownership de chaves e valores e traz um exemplo de contagem de palavras que atualiza valores.
- HashMap — Rust by Example (em inglês): um exemplo executável de lista de contatos usando
insert,get,removee laços. HashMap— documentação da std (em inglês): todos os métodos, incluindocontains_key,remove,keysevalues.Entry— documentação da std (em inglês): tudo o que você pode fazer com.entry(), comoor_insert_witheand_modify.