Introdução
Um vetor organiza valores por posição numérica. Já um HashMap<K, V> associa cada chave exclusiva do tipo K a um valor do tipo V, o que o torna útil para contagens, configurações e outros dados indexados por chave.
Você criará um pequeno mapa de contagem de palavras, recuperará e substituirá uma contagem, usará a API de entradas para palavras existentes e ausentes e percorrerá todos os valores sem depender da ordem de chaves não especificada do mapa.
Inserir e recuperar valores associados a chaves
Nesta etapa, você inserirá dois pares de chave e valor e recuperará uma contagem com segurança.
Entre no projeto e abra o código-fonte:
cd /home/labex/project/word-counts
nano src/main.rs
O caminho preparado std::collections::HashMap identifica HashMap dentro do módulo de coleções da biblioteca padrão do Rust. A linha use dá a esse caminho longo o nome local curto HashMap. Por enquanto, considere isso como a configuração preparada da importação; os caminhos e as importações serão ensinados integralmente nos módulos posteriores do Lab. HashMap<String, u32> armazena chaves de texto próprias e contagens inteiras sem sinal. Substitua o comentário Step 1a por:
let _ = counts.insert(String::from("rust"), 1);
let _ = counts.insert(String::from("safe"), 1);
insert retorna qualquer valor armazenado anteriormente para essa chave. Como essas chaves são novas, o resultado é None. O padrão let _ = ...; avalia a operação e descarta deliberadamente o resultado. Esse _ isolado é diferente de um nome como _returned_id: o curinga isolado não cria uma associação que você possa usar depois.
Substitua o comentário Step 1b por:
match counts.get("rust") {
Some(count) => println!("Rust count: {count}"),
None => println!("Rust count: missing"),
}
get aceita o texto emprestado "rust" e retorna um Option<&u32> sem mover a chave ou a contagem para fora do mapa. Salve e saia do editor. Depois, verifique e execute o programa:
cargo check
cargo run --quiet
Unique words: 2
Rust count: 1
A primeira linha comprova que as chaves são exclusivas; a segunda comprova que a consulta encontrou a contagem armazenada.
Substituir uma contagem existente
Nesta etapa, você usará insert com uma chave existente e verificará o valor que foi substituído.
Abra o código-fonte:
nano src/main.rs
Substitua o comentário Step 2 por:
let previous = counts.insert(String::from("rust"), 2);
println!("Previous rust count: {previous:?}");
Um mapa mantém apenas um valor por chave. O novo valor 2 substitui 1, e o Some(1) retornado torna essa substituição observável. Salve e saia do editor. Depois, execute:
cargo run --quiet
A nova linha deve ser:
Previous rust count: Some(1)
A contagem de palavras exclusivas continua sendo dois, porque a substituição não cria outra chave.
Atualizar contagens com a API de entradas
Nesta etapa, você incrementará uma palavra existente e uma palavra ausente usando a mesma API.
entry(key).or_insert(default) retorna uma referência mutável ao valor existente da chave ou insere o valor padrão e retorna uma referência mutável a ele. Abra o código-fonte:
nano src/main.rs
Substitua o comentário Step 3 por:
*counts.entry(String::from("safe")).or_insert(0) += 1;
*counts.entry(String::from("fast")).or_insert(0) += 1;
println!("Safe count: {}", counts["safe"]);
println!("Fast count: {}", counts["fast"]);
Leia a atualização compacta de dentro para fora. Primeiro, entry(key) seleciona o local associado à chave, esteja ele ocupado ou vazio. Em seguida, or_insert(0) garante que exista um valor e retorna uma referência mutável, &mut u32, para esse valor.
O * inicial acessa o u32 por trás dessa referência. Por fim, += 1 adiciona um e armazena a nova contagem de volta no mapa.
Assim, safe passa a ser 2, enquanto a palavra ausente fast começa em zero e passa a ser 1. Os colchetes também permitem ler um valor por chave. O acesso por índice ao mapa causaria um panic se a chave estivesse ausente; ele é seguro aqui apenas porque as duas chamadas anteriores a entry garantem que ambas as chaves existam. Continue usando get quando a ausência for possível.
Salve e saia do editor. Depois, execute:
cargo run --quiet
As linhas finais agora devem ser:
Safe count: 2
Fast count: 1
Uma única operação tratou tanto o caso de chave ocupada quanto o de chave vazia.
Percorrer sem depender da ordem das chaves
Nesta etapa, você percorrerá todas as contagens armazenadas e calculará um total independente da ordem.
Os mapas de dispersão não prometem uma ordem estável de iteração das chaves. Por isso, a verificação para iniciantes não deve esperar que as chaves sejam exibidas em uma sequência específica. O método values() visita todos os valores armazenados, o que é suficiente para calcular um total.
Abra o código-fonte e substitua o comentário Step 4:
nano src/main.rs
let mut total = 0;
for count in counts.values() {
total += count;
}
println!("Total occurrences: {total}");
Aqui, count é uma referência compartilhada, e += pode adicionar o inteiro referenciado. Salve e saia do editor. Depois, verifique e execute:
cargo check
cargo run --quiet
A última linha deve ser:
Total occurrences: 5
O total combina rust = 2, safe = 2 e fast = 1 sem depender da ordem em que esses valores são percorridos.
Resumo
Você inseriu, recuperou, substituiu e incrementou contagens associadas a chaves. Depois, percorreu todos os valores do mapa sem presumir uma ordem estável das chaves.


