---
title: "Hash Trait em Rust"
url: "https://rustlang.com.br/stdlib/hash-trait/"
markdown_url: "https://rustlang.com.br/stdlib/hash-trait.MD"
description: "Guia completo sobre o trait Hash em Rust: derive automático, implementação manual, contrato Hash+Eq, HashMap, HashSet e algoritmos."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Hash Trait em Rust

Guia completo sobre o trait Hash em Rust: derive automático, implementação manual, contrato Hash+Eq, HashMap, HashSet e algoritmos.


## O que é o Trait Hash?

O trait `Hash` permite que um valor seja transformado em um número inteiro (hash) que o identifica de forma eficiente. Esse hash é usado por estruturas de dados como `HashMap` e `HashSet` para armazenar e buscar elementos em tempo O(1) na média.

Quando você insere uma chave em um `HashMap`, o Rust:
1. Calcula o hash da chave usando `Hash`
2. Usa o hash para determinar em qual bucket armazenar o par chave-valor
3. Na busca, recalcula o hash para encontrar o bucket correto
4. Usa `Eq` para confirmar que encontrou a chave exata (tratando colisões)

Por isso, `Hash` e `Eq` trabalham juntos com um **contrato** que nunca deve ser violado.

---

## Definição do Trait

```rust
// std::hash::Hash
pub trait Hash {
    fn hash<H: Hasher>(&self, state: &mut H);

    // Implementação padrão baseada em hash()
    fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
    where
        Self: Sized,
    { ... }
}

// std::hash::Hasher (simplificado)
pub trait Hasher {
    fn finish(&self) -> u64;
    fn write(&mut self, bytes: &[u8]);

    // Métodos auxiliares com implementação padrão
    fn write_u8(&mut self, i: u8) { ... }
    fn write_u16(&mut self, i: u16) { ... }
    fn write_u32(&mut self, i: u32) { ... }
    fn write_u64(&mut self, i: u64) { ... }
    fn write_usize(&mut self, i: usize) { ... }
    fn write_i8(&mut self, i: i8) { ... }
    fn write_i16(&mut self, i: i16) { ... }
    fn write_i32(&mut self, i: i32) { ... }
    fn write_i64(&mut self, i: i64) { ... }
    fn write_isize(&mut self, i: isize) { ... }
    // ...
}
```

O método `hash` recebe um `Hasher` mutável e alimenta-o com os dados do valor. O hasher acumula esses dados e produz um `u64` final via `finish()`.

---

## O Contrato Hash + Eq

Este é o contrato mais importante a respeitar:

> **Se `a == b` (via `Eq`), então `hash(a) == hash(b)`.**

A violação desse contrato corrompe `HashMap` e `HashSet`, causando comportamentos imprevisíveis (chaves que "desaparecem", buscas que falham).

Note que o inverso **não** é obrigatório: dois valores diferentes podem ter o mesmo hash (colisão). Isso é normal e esperado.

```rust
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;

fn calcular_hash<T: Hash>(valor: &T) -> u64 {
    let mut hasher = DefaultHasher::new();
    valor.hash(&mut hasher);
    hasher.finish()
}

fn main() {
    let a = "olá";
    let b = "olá";
    let c = "mundo";

    // a == b, então hash(a) == hash(b)
    assert_eq!(a, b);
    assert_eq!(calcular_hash(&a), calcular_hash(&b));

    // a != c, hashes provavelmente diferentes (mas não garantido)
    assert_ne!(a, c);
    println!("hash('olá')   = {}", calcular_hash(&a));
    println!("hash('mundo') = {}", calcular_hash(&c));
}
```

---

## Como Implementar: derive vs impl manual

### Hash com #[derive]

A forma mais comum e recomendada:

```rust
#[derive(Debug, Hash, PartialEq, Eq)]
struct Produto {
    codigo: String,
    nome: String,
    categoria: u32,
}

fn main() {
    use std::collections::HashSet;

    let mut catalogo = HashSet::new();

    catalogo.insert(Produto {
        codigo: String::from("PRD-001"),
        nome: String::from("Teclado"),
        categoria: 1,
    });

    catalogo.insert(Produto {
        codigo: String::from("PRD-002"),
        nome: String::from("Mouse"),
        categoria: 1,
    });

    println!("Produtos no catálogo: {}", catalogo.len()); // 2
}
```

Quando derivado, `Hash` alimenta o hasher com cada campo **na ordem de declaração**. Para funcionar, todos os campos devem implementar `Hash`.

### Hash com implementação manual

Quando você quer que apenas alguns campos participem do hash (por exemplo, uma chave natural):

```rust
use std::hash::{Hash, Hasher};

#[derive(Debug)]
struct Funcionario {
    cpf: String,      // Identificador único
    nome: String,
    salario: f64,     // f64 NÃO implementa Hash!
    departamento: String,
}

// Hash e Eq baseados apenas no CPF
impl Hash for Funcionario {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.cpf.hash(state);
    }
}

impl PartialEq for Funcionario {
    fn eq(&self, other: &Self) -> bool {
        self.cpf == other.cpf
    }
}

impl Eq for Funcionario {}

fn main() {
    use std::collections::HashMap;

    let mut equipe = HashMap::new();

    equipe.insert(
        Funcionario {
            cpf: String::from("123.456.789-00"),
            nome: String::from("Ana"),
            salario: 8000.0,
            departamento: String::from("Engenharia"),
        },
        "Desenvolvedora Sênior",
    );

    // Busca pelo CPF — outros campos são irrelevantes para Hash/Eq
    let busca = Funcionario {
        cpf: String::from("123.456.789-00"),
        nome: String::new(),      // Irrelevante para busca
        salario: 0.0,
        departamento: String::new(),
    };

    if let Some(cargo) = equipe.get(&busca) {
        println!("Cargo: {}", cargo);
    }
}
```

---

## Exemplos Práticos

### Exemplo 1: HashMap com chave customizada

```rust
use std::collections::HashMap;

#[derive(Debug, Hash, PartialEq, Eq, Clone)]
struct Coordenada {
    x: i32,
    y: i32,
}

impl Coordenada {
    fn nova(x: i32, y: i32) -> Self {
        Coordenada { x, y }
    }
}

fn main() {
    let mut mapa: HashMap<Coordenada, String> = HashMap::new();

    mapa.insert(Coordenada::nova(0, 0), "Origem".into());
    mapa.insert(Coordenada::nova(1, 0), "Leste".into());
    mapa.insert(Coordenada::nova(0, 1), "Norte".into());
    mapa.insert(Coordenada::nova(-1, 0), "Oeste".into());

    let posicao = Coordenada::nova(0, 0);
    if let Some(nome) = mapa.get(&posicao) {
        println!("Posição (0,0): {}", nome); // Origem
    }

    // Iterar sobre todas as entradas
    for (coord, nome) in &mapa {
        println!("({}, {}) = {}", coord.x, coord.y, nome);
    }
}
```

### Exemplo 2: HashSet para deduplicação

```rust
use std::collections::HashSet;

#[derive(Debug, Hash, PartialEq, Eq)]
struct Tag {
    nome: String,
}

impl Tag {
    fn nova(nome: impl Into<String>) -> Self {
        Tag { nome: nome.into().to_lowercase() }
    }
}

fn main() {
    let mut tags = HashSet::new();

    // Insere tags normalizadas
    tags.insert(Tag::nova("Rust"));
    tags.insert(Tag::nova("rust"));     // Duplicata! Não será inserida
    tags.insert(Tag::nova("RUST"));     // Duplicata!
    tags.insert(Tag::nova("programação"));
    tags.insert(Tag::nova("web"));

    println!("Tags únicas: {}", tags.len()); // 3

    // Verifica existência
    let busca = Tag::nova("rust");
    println!("Contém 'rust'? {}", tags.contains(&busca)); // true

    // Operações de conjunto
    let mut outras_tags = HashSet::new();
    outras_tags.insert(Tag::nova("rust"));
    outras_tags.insert(Tag::nova("linux"));
    outras_tags.insert(Tag::nova("web"));

    let comuns: HashSet<_> = tags.intersection(&outras_tags).collect();
    println!("Tags em comum: {}", comuns.len()); // 2 (rust, web)
}
```

### Exemplo 3: Hash para enums complexas

```rust
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

#[derive(Debug, Clone)]
enum Permissao {
    Ler,
    Escrever,
    Admin { nivel: u8 },
    Custom(String),
}

impl Hash for Permissao {
    fn hash<H: Hasher>(&self, state: &mut H) {
        // Hash o discriminante da variante primeiro
        std::mem::discriminant(self).hash(state);

        // Hash dos dados internos
        match self {
            Permissao::Ler => {}
            Permissao::Escrever => {}
            Permissao::Admin { nivel } => nivel.hash(state),
            Permissao::Custom(nome) => nome.hash(state),
        }
    }
}

impl PartialEq for Permissao {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Permissao::Ler, Permissao::Ler) => true,
            (Permissao::Escrever, Permissao::Escrever) => true,
            (Permissao::Admin { nivel: a }, Permissao::Admin { nivel: b }) => a == b,
            (Permissao::Custom(a), Permissao::Custom(b)) => a == b,
            _ => false,
        }
    }
}

impl Eq for Permissao {}

fn main() {
    let mut permissoes: HashMap<Permissao, Vec<String>> = HashMap::new();

    permissoes
        .entry(Permissao::Ler)
        .or_default()
        .push("Ana".into());

    permissoes
        .entry(Permissao::Ler)
        .or_default()
        .push("Bruno".into());

    permissoes
        .entry(Permissao::Admin { nivel: 1 })
        .or_default()
        .push("Carlos".into());

    for (perm, usuarios) in &permissoes {
        println!("{:?}: {:?}", perm, usuarios);
    }
}
```

### Exemplo 4: Usando um Hasher alternativo

```rust
use std::collections::HashMap;
use std::hash::{BuildHasherDefault, Hasher};

// Hasher simples (NÃO use em produção — apenas para demonstração)
#[derive(Default)]
struct HasherSimples {
    hash: u64,
}

impl Hasher for HasherSimples {
    fn finish(&self) -> u64 {
        self.hash
    }

    fn write(&mut self, bytes: &[u8]) {
        for &byte in bytes {
            self.hash = self.hash.wrapping_mul(31).wrapping_add(byte as u64);
        }
    }
}

type BuildHasherSimples = BuildHasherDefault<HasherSimples>;

fn main() {
    // HashMap com hasher customizado
    let mut mapa: HashMap<String, i32, BuildHasherSimples> =
        HashMap::with_hasher(BuildHasherSimples::default());

    mapa.insert("um".into(), 1);
    mapa.insert("dois".into(), 2);
    mapa.insert("três".into(), 3);

    println!("{:?}", mapa);

    // Na prática, use crates como `ahash` ou `rustc-hash`
    // para hashers mais rápidos que o padrão (SipHash)
}
```

### Exemplo 5: Por que f64 não implementa Hash

```rust
use std::collections::HashMap;

// f64 não implementa Hash nem Eq por causa de NaN
// NaN != NaN viola o contrato de Eq
// Duas representações de 0.0 (+0.0 e -0.0) são == mas têm bits diferentes

// Solução: wrapper que trata esses casos
#[derive(Debug, Clone, Copy)]
struct FloatOrdenado(f64);

impl FloatOrdenado {
    fn new(v: f64) -> Option<Self> {
        if v.is_nan() {
            None  // Rejeita NaN
        } else {
            Some(FloatOrdenado(v))
        }
    }

    fn to_bits_normalizado(&self) -> u64 {
        if self.0 == 0.0 {
            0u64  // Normaliza -0.0 e +0.0 para o mesmo valor
        } else {
            self.0.to_bits()
        }
    }
}

impl std::hash::Hash for FloatOrdenado {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.to_bits_normalizado().hash(state);
    }
}

impl PartialEq for FloatOrdenado {
    fn eq(&self, other: &Self) -> bool {
        self.to_bits_normalizado() == other.to_bits_normalizado()
    }
}

impl Eq for FloatOrdenado {}

fn main() {
    let mut contagem: HashMap<FloatOrdenado, u32> = HashMap::new();

    let temperaturas = [36.5, 37.0, 36.5, 38.0, 37.0, 36.5];

    for &temp in &temperaturas {
        if let Some(chave) = FloatOrdenado::new(temp) {
            *contagem.entry(chave).or_insert(0) += 1;
        }
    }

    for (temp, qtd) in &contagem {
        println!("{:.1}°C apareceu {} vez(es)", temp.0, qtd);
    }
}
```

---

## Padrões e Boas Práticas

1. **Derive `Hash` junto com `PartialEq` e `Eq`**: Sempre que derivar `Hash`, derive os outros dois. O contrato exige consistência entre eles.

2. **Hash e Eq devem usar os mesmos campos**: Se `PartialEq` compara apenas o campo `id`, então `Hash` deve usar apenas o campo `id`. Nunca inclua campos em Hash que não participam de Eq.

3. **Não use f64 como chave de HashMap**: Floats não implementam `Hash` nem `Eq` por boas razões. Use um wrapper normalizado ou converta para inteiro.

4. **`std::mem::discriminant` para enums**: Ao implementar `Hash` manualmente para enums, use `discriminant()` para incluir a variante no hash.

5. **O hasher padrão é SipHash**: Rust usa SipHash-1-3, que é resistente a HashDoS. Para performance máxima (quando segurança não é preocupação), considere crates como `ahash` ou `rustc-hash`.

6. **Ordem dos campos importa**: O hash de `(1, 2)` deve ser diferente de `(2, 1)`. O derive cuida disso automaticamente, mas em implementações manuais, alimente o hasher na mesma ordem consistente.

7. **Hash é determinístico por execução**: O hash do mesmo valor pode diferir entre execuções do programa (por causa da seed aleatória). Não persista hashes em disco.

---

## Veja Também

- [Eq e Ord](/stdlib/eq-ord/) — o contrato Eq que Hash depende
- [AsRef e Borrow](/stdlib/asref-borrow/) — Borrow mantém o contrato Hash+Eq
- [Default Trait](/stdlib/default/) — usado com `or_default()` em HashMap
- [Display e Debug](/stdlib/display-debug/) — frequentemente derivados junto com Hash
- [HashMap](/stdlib/hashmap/) — a principal estrutura que consome Hash
- [HashSet](/stdlib/hashset/) — conjunto baseado em Hash+Eq
