Rust: Coleções em Rust

Última atualização: 2026-08-26

HashMap e HashSet são as coleções baseadas em hash mais utilizadas na biblioteca padrão do Rust — o HashMap armazena mapeamentos de chave-valor, enquanto o HashSet armazena um conjunto de elementos únicos.

Se o Vec serve para “armazenar itens em ordem”, o HashMap serve para “encontrar itens pelo nome” — você não precisa se lembrar do índice; basta saber a chave. Já o HashSet fornece a resposta definitiva para a pergunta: “Este item está presente?”


1. O que você vai aprender


2. A história de um sistema eleitoral

(1) Dificuldade: Usar dois Vecs para armazenar o número de ingressos

Anna está desenvolvendo um sistema de votação para a turma e precisa contabilizar o número de votos que cada candidato recebe.

No início, ela usou dois objetos Vec:

RUST
let mut candidates = Vec::new();
let mut votes = Vec::new();

candidates.push("Alice");
votes.push(0);

candidates.push("Bob");
votes.push(0);

// Vote for Alice
let pos = candidates.iter().position(|&c| c == "Alice").unwrap();
votes[pos] += 1;

// Search Bob the number of votes
let pos = candidates.iter().position(|&c| c == "Bob").unwrap();
println!("Bob the number of votes: {}", votes[pos]);

Gerenciar dados com dois Vecs paralelos apresenta um problema óbvio: manter os dois Vecs sincronizados é complicado — é fácil esquecer de atualizar um dos Vecs ao adicionar ou remover candidatos. Além disso, a busca por um candidato requer uma busca linear de complexidade O(n), o que torna o processo mais lento à medida que o número de candidatos aumenta. Em termos de legibilidade do código, a relação entre candidates[i] e votes[i] é implícita, o que dificulta a compreensão por parte de novos desenvolvedores.

(2) A abordagem do HashMap no Rust

RUST
use std::collections::HashMap;

fn main() {
    let mut votes = HashMap::new();

    // Vote for a candidate
    *votes.entry("Alice").or_insert(0) += 1;
    *votes.entry("Bob").or_insert(0) += 1;
    *votes.entry("Alice").or_insert(0) += 1;  // Re-submit Alice
    *votes.entry("Charlie").or_insert(0) += 1;

    // Check the number of votes
    for (candidate, count) in &votes {
        println!("{}: {} votes", candidate, count);
    }

    // Search for a Specific Candidate
    println!("Alice the number of votes: {}", votes.get("Alice").unwrap());
}

Resultado:

TEXT 📖 Somente leitura
Alice: 2 votes
Bob: 1 votes
Charlie: 1 votes
Alice the number of votes: 2

O HashMap é uma tabela de mapeamento chave-valor: key -> value. A API entry lida com elegância com cenários em que “se a chave não existir, insira o valor padrão; se existir, atualize-a”. O método get pesquisa chaves com complexidade de tempo O(1). Não há mais necessidade de manter dois Vecs sincronizados.


3. Visão geral do HashMap e do HashSet

(1) Mapa conceitual

100%
graph TB
    A[Hash-Based Sets] --> B[HashMap<K, V>]
    A --> C[HashSet<T>]
    B --> B1[insert: Insert a key-value pair]
    B --> B2[get: Retrieving a Value by Key]
    B --> B3[entry: Elegant Insertion/Update]
    B --> B4[remove: Delete a key-value pair]
    B --> B5[contains_key: Check if a key exists]
    B --> B6[iter: Iterate through all key-value pairs]
    C --> C1[insert: Add an element]
    C --> C2[contains: Check if an element is included]
    C --> C3[union: Union Operation]
    C --> C4[intersection: Set Intersection]
    C --> C5[difference: Difference Set Operations]
    C --> C6[symmetric_difference: Symmetric difference set]

(2) Comparação entre tipos de conjuntos

Característica Vec<T> HashMap<K, V> HashSet<T>
Armazenamento Sequências ordenadas Pares chave-valor não ordenados Elementos únicos não ordenados
Consulta Pesquisa linear O(n) Consulta em tabela hash O(1) Consulta em tabela hash O(1)
Inserção O(1) Adicionar ao final O(1) em média O(1) em média
Remoção de duplicatas Verificação manual Remoção automática de duplicatas de chaves Remoção automática de duplicatas de elementos
Memória Baixa (armazenamento contíguo) Média (sobrecarga da tabela hash) Média (sobrecarga da tabela hash)
Casos de uso Acesso sequencial, pequenos conjuntos de dados Mapeamento chave-valor, consultas rápidas Operações em conjuntos, deduplicação

(3) Referência rápida aos métodos comuns do HashMap

Método Tipo de retorno Descrição
insert(k, v) Option<V> Inserir um par chave-valor e retornar o valor anterior
get(&k) Option<&V> Pesquisar por chave
get_mut(&k) Option<&mut V> Localizar referências a variáveis por chave
remove(&k) Option<V> Excluir um par chave-valor e retornar o valor excluído
contains_key(&k) bool A chave existe?
entry(k) Entry<K,V> Recuperar um registro para inserir/atualizar
keys() Keys<K,V> Percorrer todas as chaves
values() Values<K,V> Percorrer todos os valores
len() usize Número de pares chave-valor
is_empty() bool Está vazio?
clear() () Limpar todos os pares chave-valor
drain() Drain<K,V> Remover e devolver todos os pares chave-valor

(4) Operações com conjuntos no HashSet

Operação Método Símbolo matemático Descrição
União union(&other) A ∪ B Todos os elementos dos dois conjuntos
Interseção intersection(&other) A ∩ B Elementos comuns aos dois conjuntos
Diferença entre conjuntos difference(&other) A - B Elementos que estão em A, mas não em B
Diferença de simetria symmetric_difference(&other) A △ B Elementos presentes em apenas um conjunto
Subconjunto is_subset(&other) A ⊆ B Todos os elementos de A estão em B
Supersérie is_superset(&other) A ⊇ B A contém todos os elementos de B

4. Exemplos de HashMap e HashSet

▶ Exemplo 1: API básica do HashMap — Sistema de contagem de votos (Dificuldade ⭐⭐)

RUST
// ============================================
// Voting Tally System:Display HashMap Basic API
// ============================================

use std::collections::HashMap;

fn main() {
    // Create a new empty HashMap
    let mut vote_counts: HashMap<String, u32> = HashMap::new();

    // --- insert ---
    // Insert key-value pairs (overwrites existing value)
    vote_counts.insert(String::from("Alice"), 0);
    vote_counts.insert(String::from("Bob"), 0);
    vote_counts.insert(String::from("Charlie"), 0);

    println!("After initial insert:");
    print_votes(&vote_counts);

    // --- get ---
    // Get a value by key (returns Option<&V>)
    let alice_votes = vote_counts.get("Alice");
    match alice_votes {
        Some(count) => println!("Alice's votes (via get): {}", count),
        None => println!("Alice not found"),
    }

    // --- entry API ---
    // The idiomatic way: insert or update
    // entry() returns an Entry enum, or_insert() inserts default if missing
    println!("\n--- Voting round ---");
    let candidates = ["Alice", "Bob", "Alice", "Charlie", "Alice", "Bob", "David"];
    for name in &candidates {
        let count = vote_counts.entry(String::from(*name)).or_insert(0);
        *count += 1;
        println!("Voted for {} (total: {})", name, count);
    }

    // --- contains_key ---
    println!("\n--- Checking candidates ---");
    for name in &["Alice", "David", "Eve"] {
        if vote_counts.contains_key(*name) {
            println!("{} is a candidate with {} votes", name, vote_counts.get(*name).unwrap());
        } else {
            println!("{} is NOT a candidate", name);
        }
    }

    // --- len and is_empty ---
    println!("\nTotal candidates: {}", vote_counts.len());
    println!("Is empty: {}", vote_counts.is_empty());

    // --- Final results ---
    println!("\n--- Final Results ---");
    print_votes(&vote_counts);
}

fn print_votes(votes: &HashMap<String, u32>) {
    // Note: HashMap iteration order is NOT guaranteed
    for (name, count) in votes {
        println!("  {}: {} votes", name, count);
    }
}

Resultado:

TEXT 📖 Somente leitura
After initial insert:
  Alice: 0 votes
  Charlie: 0 votes
  Bob: 0 votes

Alice's votes (via get): 0

--- Voting round ---
Voted for Alice (total: 1)
Voted for Bob (total: 1)
Voted for Alice (total: 2)
Voted for Charlie (total: 1)
Voted for Alice (total: 3)
Voted for Bob (total: 2)
Voted for David (total: 1)

--- Checking candidates ---
Alice is a candidate with 3 votes
David is a candidate with 1 votes
Eve is NOT a candidate

Total candidates: 4
Is empty: false

--- Final Results ---
  Alice: 3 votes
  Charlie: 1 votes
  David: 1 votes
  Bob: 2 votes

entry(key).or_insert(default) é a forma idiomática mais comum de usar o HashMap — se a chave não existir, ele insere um valor padrão e retorna uma referência a ele; se a chave existir, ele simplesmente retorna uma referência a ela. Combinado com *count += 1, permite realizar uma operação de “inserir ou atualizar” em uma única linha. get retorna Option<&V> e nunca causa um panic.


▶ Exemplo 2: Regras de propriedade do HashMap e tipos de valores (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// HashMap Ownership Rules:What types are eligible? key/value
// ============================================

use std::collections::HashMap;

#[derive(Debug, Hash, Eq, PartialEq)]
struct ProductId(u32);

#[derive(Debug, Clone)]
struct Product {
    name: String,
    price: f64,
    stock: u32,
}

fn main() {
    // --- Rule 1: Owned types as keys ---
    // String (owned) can be a key; &str (borrowed) needs lifetime management
    let mut inventory: HashMap<String, Product> = HashMap::new();

    let product = Product {
        name: String::from("Rust Book"),
        price: 29.99,
        stock: 100,
    };

    // insert takes ownership of key and value
    inventory.insert(String::from("RB-001"), product);
    // println!("{:?}", product);  // ❌ product was moved into the HashMap

    // --- Rule 2: Inserting a reference ---
    // Borrowed keys need lifetime annotations on the HashMap
    // This works because the string literals have 'static lifetime
    let mut lookup: HashMap<&str, u32> = HashMap::new();
    lookup.insert("apple", 5);
    lookup.insert("banana", 3);
    println!("Lookup table: {:?}", lookup);

    // --- Rule 3: Getting values returns references ---
    // get() returns Option<&V>, not V
    let stock_ref = inventory.get("RB-001");
    match stock_ref {
        Some(p) => println!("Product: {}, price: {}", p.name, p.price),
        None => println!("Not found"),
    }
    // inventory is still valid (we only borrowed)

    // --- Rule 4: Custom types as keys ---
    // Keys must implement Eq + Hash
    let mut product_map: HashMap<ProductId, String> = HashMap::new();
    product_map.insert(ProductId(1), String::from("Laptop"));
    product_map.insert(ProductId(2), String::from("Mouse"));

    // --- Rule 5: Updating values with get_mut ---
    // get_mut() returns Option<&mut V> for mutable access
    if let Some(product) = inventory.get_mut("RB-001") {
        product.stock -= 1;  // Sell one unit
        println!("Updated stock: {}", product.stock);
    }

    // --- Rule 6: The entry API for sophisticated updates ---
    let mut word_count: HashMap<String, u32> = HashMap::new();
    let text = "hello world hello rust hello again";

    for word in text.split_whitespace() {
        // or_insert returns &mut V, which we dereference and increment
        let counter = word_count.entry(String::from(word)).or_insert(0);
        *counter += 1;
    }
    println!("\nWord count: {:?}", word_count);

    // Advanced: modify entry with and_modify + or_insert
    let mut scores: HashMap<String, u32> = HashMap::new();
    for team in &["red", "blue", "red", "green", "blue", "red"] {
        scores.entry(String::from(*team))
            .and_modify(|count| *count += 1)  // if exists, increment
            .or_insert(1);                     // if not, insert 1
    }
    println!("Scores: {:?}", scores);
}

Resultado:

TEXT 📖 Somente leitura
Lookup table: {"banana": 3, "apple": 5}
Product: Rust Book, price: 29.99
Updated stock: 99

Word count: {"again": 1, "hello": 2, "rust": 1, "world": 1}
Scores: {"green": 1, "blue": 2, "red": 3}

Regras de propriedade do HashMap: Ao inserir, a propriedade da chave e do valor é transferida para o HashMap. get retorna uma referência (&V) e não transfere a propriedade. O tipo da chave deve implementar o trait Eq + Hash (os tipos primitivos e String implementam isso por padrão). As chamadas encadeadas entry + and_modify + or_insert são um padrão elegante exclusivo do Rust.


▶ Exemplo 3: Remoção de duplicatas de um HashSet e operações com conjuntos (Dificuldade ⭐⭐)

RUST
// ============================================
// HashSet:Remove duplicates、Intersection、Union、Difference Set Operations
// ============================================

use std::collections::HashSet;

fn main() {
    // --- Basic HashSet: deduplication ---
    println!("--- HashSet Deduplication ---");
    let mut unique_numbers: HashSet<i32> = HashSet::new();

    let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
    for &n in &numbers {
        unique_numbers.insert(n);
    }
    println!("Original: {:?}", &numbers[..]);
    println!("Unique: {:?}", unique_numbers);
    println!("Count: {} (original: {})", unique_numbers.len(), numbers.len());

    // --- contains ---
    println!("\n--- Contains Check ---");
    for &n in &[1, 7, 9] {
        if unique_numbers.contains(&n) {
            println!("{} is in the set", n);
        } else {
            println!("{} is NOT in the set", n);
        }
    }

    // --- Set operations ---
    println!("\n--- Set Operations ---");

    let set_a: HashSet<i32> = [1, 2, 3, 4, 5].iter().cloned().collect();
    let set_b: HashSet<i32> = [4, 5, 6, 7, 8].iter().cloned().collect();

    println!("Set A: {:?}", set_a);
    println!("Set B: {:?}", set_b);

    // Union: elements in A OR B
    let union: HashSet<&i32> = set_a.union(&set_b).collect();
    println!("Union (A ∪ B): {:?}", union);

    // Intersection: elements in A AND B
    let intersection: HashSet<&i32> = set_a.intersection(&set_b).collect();
    println!("Intersection (A ∩ B): {:?}", intersection);

    // Difference: elements in A but NOT in B
    let diff_ab: HashSet<&i32> = set_a.difference(&set_b).collect();
    println!("Difference (A - B): {:?}", diff_ab);

    let diff_ba: HashSet<&i32> = set_b.difference(&set_a).collect();
    println!("Difference (B - A): {:?}", diff_ba);

    // Symmetric difference: elements in A or B but NOT both
    let sym_diff: HashSet<&i32> = set_a.symmetric_difference(&set_b).collect();
    println!("Symmetric Difference: {:?}", sym_diff);

    // --- Practical example: finding common friends ---
    println!("\n--- Practical: Common Friends ---");

    let alice_friends: HashSet<&str> =
        ["Bob", "Charlie", "David", "Eve"].iter().cloned().collect();
    let bob_friends: HashSet<&str> =
        ["Alice", "Charlie", "Eve", "Frank"].iter().cloned().collect();

    println!("Alice's friends: {:?}", alice_friends);
    println!("Bob's friends: {:?}", bob_friends);

    // Mutual friends (intersection)
    let mutual: HashSet<&&str> = alice_friends.intersection(&bob_friends).collect();
    println!("Mutual friends: {:?}", mutual);

    // Friends only Alice knows (difference)
    let alice_only: HashSet<&&str> = alice_friends.difference(&bob_friends).collect();
    println!("Only Alice knows: {:?}", alice_only);

    // All unique friends (union)
    let all_friends: HashSet<&&str> = alice_friends.union(&bob_friends).collect();
    println!("All unique friends: {:?}", all_friends);
}

Resultado:

TEXT 📖 Somente leitura
--- HashSet Deduplication ---
Original: [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
Unique: {3, 2, 1, 6, 4, 9, 5}
Count: 7 (original: 11)

--- Contains Check ---
1 is in the set
7 is NOT in the set
9 is in the set

--- Set Operations ---
Set A: {2, 3, 4, 5, 1}
Set B: {4, 7, 6, 5, 8}
Union (A ∪ B): {7, 2, 3, 6, 4, 5, 1, 8}
Intersection (A ∩ B): {4, 5}
Difference (A - B): {2, 3, 1}
Difference (B - A): {6, 7, 8}
Symmetric Difference: {1, 2, 3, 6, 7, 8}

--- Practical: Common Friends ---
Alice's friends: {"Charlie", "David", "Eve", "Bob"}
Bob's friends: {"Charlie", "Frank", "Alice", "Eve"}
Mutual friends: {"Charlie", "Eve"}
Only Alice knows: {"David", "Bob"}
All unique friends: {"Charlie", "David", "Frank", "Alice", "Eve", "Bob"}

As quatro principais operações de conjuntos do HashSet: union (união — todos os elementos), intersection (interseção — elementos comuns), difference (diferença — elementos presentes em A, mas não em B), symmetric_difference (diferença simétrica — elementos ausentes em ambos os conjuntos). Esses métodos retornam iteradores; é necessário usar .collect() para coletar os resultados em um novo HashSet.


▶ Exemplo 4: Iteração por um HashMap e estratégias de seleção para coleções (Dificuldade ⭐⭐)

RUST
// ============================================
// Iterate HashMap + Comparison of Set Selection Strategies
// ============================================

use std::collections::HashMap;

fn main() {
    // --- Build a sample dataset ---
    let mut sales: HashMap<String, f64> = HashMap::new();
    sales.insert(String::from("Laptop"), 1200.0);
    sales.insert(String::from("Mouse"), 25.0);
    sales.insert(String::from("Keyboard"), 80.0);
    sales.insert(String::from("Monitor"), 350.0);
    sales.insert(String::from("Headphones"), 150.0);

    // --- Method 1: Iterate over key-value pairs ---
    println!("--- All Products (iter) ---");
    for (product, revenue) in &sales {
        println!("  {}: ${:.2}", product, revenue);
    }

    // --- Method 2: Iterate over keys only ---
    println!("\n--- Product Names (keys) ---");
    for product in sales.keys() {
        println!("  - {}", product);
    }

    // --- Method 3: Iterate over values only ---
    println!("\n--- Revenue Values (values) ---");
    let total: f64 = sales.values().sum();
    println!("  Total revenue: ${:.2}", total);
    println!("  Average: ${:.2}", total / sales.len() as f64);

    // --- Method 4: Mutable iteration over values ---
    println!("\n--- Apply 10% Discount (values_mut) ---");
    for revenue in sales.values_mut() {
        *revenue *= 0.9;  // Apply 10% discount
    }
    for (product, revenue) in &sales {
        println!("  {}: ${:.2}", product, revenue);
    }

    // --- Method 5: drain to consume the HashMap ---
    let mut backup = sales.clone();
    println!("\n--- Drain (consumes HashMap) ---");
    while let Some((product, revenue)) = backup.drain().next() {
        println!("  Removed: {} (${:.2})", product, revenue);
    }
    println!("  backup is empty: {}", backup.is_empty());

    // --- When to use what: Collection selection guide ---
    println!("\n--- Collection Selection Guide ---");

    // Scenario 1: Vec (ordered, indexed access)
    let mut todo_list: Vec<&str> = Vec::new();
    todo_list.push("Buy milk");
    todo_list.push("Write report");
    todo_list.push("Call mom");
    println!("Vec (ordered todo list):");
    for (i, item) in todo_list.iter().enumerate() {
        println!("  {}. {}", i + 1, item);
    }

    // Scenario 2: HashMap (key-value lookup)
    let mut phone_book: HashMap<&str, &str> = HashMap::new();
    phone_book.insert("Alice", "123-4567");
    phone_book.insert("Bob", "987-6543");
    println!("HashMap (phone book):");
    println!("  Alice's number: {}", phone_book.get("Alice").unwrap());

    // Scenario 3: HashSet (membership check)
    let mut admin_users: HashSet<&str> = HashSet::new();
    admin_users.insert("admin");
    admin_users.insert("root");
    let user = "admin";
    println!("HashSet (admin check):");
    println!("  Is '{}' admin? {}", user, admin_users.contains(user));
}

// Import HashSet for the last scenario
use std::collections::HashSet;

Resultado:

TEXT 📖 Somente leitura
--- All Products (iter) ---
  Laptop: $1200.00
  Mouse: $25.00
  Keyboard: $80.00
  Monitor: $350.00
  Headphones: $150.00

--- Product Names (keys) ---
  - Laptop
  - Mouse
  - Keyboard
  - Monitor
  - Headphones

--- Revenue Values (values) ---
  Total revenue: $1805.00
  Average: $361.00

--- Apply 10% Discount (values_mut) ---
  Laptop: $1080.00
  Mouse: $22.50
  Keyboard: $72.00
  Monitor: $315.00
  Headphones: $135.00

--- Drain (consumes HashMap) ---
  Removed: Laptop ($1080.00)
  Removed: Mouse ($22.50)
  Removed: Keyboard ($72.00)
  Removed: Monitor ($315.00)
  Removed: Headphones ($135.00)
  backup is empty: true

--- Collection Selection Guide ---
Vec (ordered todo list):
  1. Buy milk
  2. Write report
  3. Call mom
HashMap (phone book):
  Alice's number: 123-4567
HashSet (admin check):
  Is 'admin' admin? true

Formas de iterar sobre um HashMap: iter() Itera sobre todos os pares chave-valor; keys() Itera apenas sobre as chaves; values() Itera apenas sobre os valores; values_mut() Itera sobre os valores em ordem variável; drain() Consome e remove todos os elementos. Ao escolher um tipo de coleção: se você precisar de uma coleção ordenada e duplicável com acesso baseado em índice → Vec; se precisar de mapeamento chave-valor e consultas rápidas → HashMap; se precisar de deduplicação, operações de coleção e verificações de pertencimento → HashSet.


▶ Exemplo 5: Exercício abrangente — Análise de frequência de palavras e análise de texto (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example:HashMap + HashSet Text Analysis
// ============================================

use std::collections::{HashMap, HashSet};

fn word_frequency(text: &str) -> HashMap<String, u32> {
    let mut freq: HashMap<String, u32> = HashMap::new();
    for word in text.split_whitespace() {
        let clean: String = word.chars()
            .filter(|c| c.is_alphabetic())
            .map(|c| c.to_lowercase().next().unwrap())
            .collect();
        if !clean.is_empty() {
            *freq.entry(clean).or_insert(0) += 1;
        }
    }
    freq
}

fn unique_words(text: &str) -> HashSet<String> {
    text.split_whitespace()
        .map(|w| w.to_lowercase())
        .collect()
}

fn top_n(freq: &HashMap<String, u32>, n: usize) -> Vec<(&str, u32)> {
    let mut entries: Vec<_> = freq.iter().map(|(k, &v)| (k.as_str(), v)).collect();
    entries.sort_by(|a, b| b.1.cmp(&a.1));
    entries.into_iter().take(n).collect()
}

fn main() {
    let text1 = "the cat sat on the mat and the cat slept on the mat";
    let text2 = "the dog ran on the grass and the dog slept on the rug";

    println!("=== Text 1 Word Frequency ===");
    let freq1 = word_frequency(text1);
    for (word, count) in top_n(&freq1, 5) {
        println!("  '{}': {} times", word, count);
    }

    println!("\n=== Text 2 Word Frequency ===");
    let freq2 = word_frequency(text2);
    for (word, count) in top_n(&freq2, 5) {
        println!("  '{}': {} times", word, count);
    }

    let words1 = unique_words(text1);
    let words2 = unique_words(text2);

    let common: HashSet<_> = words1.intersection(&words2).collect();
    println!("\nCommon Vocabulary: {:?}", common);

    let only1: HashSet<_> = words1.difference(&words2).collect();
    println!("Text1Exclusive: {:?}", only1);

    let only2: HashSet<_> = words2.difference(&words1).collect();
    println!("Text2Exclusive: {:?}", only2);

    let all: HashSet<_> = words1.union(&words2).collect();
    println!("Total number of words: {}", all.len());
}

Resultado:

TEXT 📖 Somente leitura
=== Text 1 Word Frequency ===
  'the': 3 times
  'cat': 2 times
  'on': 2 times
  'mat': 2 times
  'sat': 1 times

=== Text 2 Word Frequency ===
  'the': 3 times
  'dog': 2 times
  'on': 2 times
  'grass': 1 times
  'ran': 1 times

Common Vocabulary: {"the", "and", "on", "slept"}
Text1Exclusive: {"mat", "cat", "sat"}
Text2Exclusive: {"ran", "rug", "grass", "dog"}

Total number of words: 11

word_frequency Conte de forma elegante usando o método entry().or_insert(); unique_words remova automaticamente as duplicatas usando um HashSet; intersection/difference/union implementem operações de conjunto. HashMap + HashSet é a combinação perfeita para análise de texto.


❓ Perguntas Frequentes

P: Que característica uma chave do HashMap deve satisfazer? R: Uma chave deve implementar a característica Eq + Hash. Todos os tipos primitivos (i32, u32, String, bool) a implementam. Tipos personalizados exigem #[derive(Hash, Eq, PartialEq)]. f64 não implementa Eq (porque NaN != NaN), portanto não pode ser usado diretamente como chave.

P: Qual é a diferença entre a API entry e um insert direto? R: entry não sobrescreve os valores existentes, enquanto insert os sobrescreve diretamente. entry(key).or_insert(value) insere apenas se a chave não existir; se ela existir, retorna uma referência ao valor existente. insert sempre sobrescreve o valor antigo e retorna Option<V> (o valor antigo). entry é a forma convencional de escrever “inserir ou atualizar”.

P: A ordem de iteração de um HashMap é fixa? R: Não, não é! A ordem de iteração de um HashMap não é ordenada. Ela pode variar a cada execução. Se você precisar de um mapeamento chave-valor ordenado, pode usar BTreeMap (ordenado por chave). Se você precisar apenas de consultas rápidas, o desempenho O(1) de um HashMap é melhor.

P: O que é mais eficiente para remover duplicatas: HashSet ou Vec? R: O HashSet é muito mais rápido ao lidar com grandes conjuntos de dados. A remoção de duplicatas com o Vec leva tempo O(n²) (já que cada elemento precisa ser comparado com todos os anteriores), enquanto a operação insert no HashSet tem uma complexidade de tempo média de O(1). No entanto, o HashSet consome mais memória e não preserva a ordem. Se você precisar preservar a ordem, considere usar Vec combinado com HashSet.

P: O que o método entry de HashMap retorna? R: Ele retorna a enumeração Entry, que possui duas variantes: Occupied(Entry) e Vacant(Entry). or_insert(default) insere um valor padrão e retorna uma referência quando o slot está Vago, e retorna uma referência ao valor existente quando ele está Ocupado. and_modify(fn) modifica o valor quando o slot está Ocupado. Esses métodos podem ser encadeados.

P: Quando se deve usar um HashMap e quando se deve usar um BTreeMap? R: Use um HashMap (O(1)) para consultas rápidas e um BTreeMap (O(log n)) para percorrer a estrutura de forma ordenada. As chaves em um HashMap não estão ordenadas, mas as consultas são rápidas; as chaves em um BTreeMap estão ordenadas (por exemplo, exibidas em ordem alfabética), mas as consultas são um pouco mais lentas. Se a ordem for importante, use um BTreeMap.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Crie uma variável HashMap<String, u32> para armazenar os preços das frutas (“maçã”=5, “banana”=3, “laranja”=4). Escreva uma função fn total_cost(items: &[&str], prices: &HashMap<String, u32>) -> u32 para calcular o preço total do carrinho de compras. Teste o carrinho de compras ["apple", "banana", "apple"] na função principal.

  2. Dificuldade ⭐⭐: Escreva uma função fn word_frequency(text: &str) -> HashMap<String, u32> que conte o número de vezes que cada palavra aparece em um texto. Use a API entry. Na função principal, teste o texto “the quick brown fox jumps over the lazy dog the fox” e imprima os resultados.

  3. Dificuldade ⭐⭐⭐: Crie uma lista de alunos de duas turmas (HashSet<&str>). A Turma A tem ["Alice", "Bob", "Charlie", "David"], e a Turma B tem ["Charlie", "David", "Eve", "Frank"]. Escreva uma função fn analyze_classes(a: &HashSet<&str>, b: &HashSet<&str>) que imprima: os alunos de ambas as turmas (interseção), os alunos apenas da Turma A (diferença), todos os alunos únicos (união) e os alunos presentes em apenas uma turma (diferença simétrica). Chame a função na função principal e imprima os resultados.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%