---
title: "Display e Debug Traits em Rust"
url: "https://rustlang.com.br/stdlib/display-debug/"
markdown_url: "https://rustlang.com.br/stdlib/display-debug.MD"
description: "Guia completo sobre os traits Display e Debug em Rust: formatação de saída para usuários e desenvolvedores com exemplos práticos em português."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Display e Debug Traits em Rust

Guia completo sobre os traits Display e Debug em Rust: formatação de saída para usuários e desenvolvedores com exemplos práticos em português.


## O que são Display e Debug?

Em Rust, os traits `fmt::Display` e `fmt::Debug` controlam como um tipo é convertido em texto. Eles são fundamentais para qualquer programa, pois determinam o que aparece quando você usa `println!`, `format!`, `write!` e outros macros de formatação.

- **`Display`** (`{}`) produz saída voltada para o **usuário final** — limpa, legível e amigável.
- **`Debug`** (`{:?}`) produz saída voltada para o **desenvolvedor** — detalhada, com nomes de campos e estrutura interna.

Praticamente todo tipo que você cria em Rust deveria implementar pelo menos `Debug`. Já `Display` é implementado quando o tipo tem uma representação textual natural que faz sentido para o usuário.

---

## Definição dos Traits

### fmt::Debug

```rust
// Definido em std::fmt
pub trait Debug {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
```

### fmt::Display

```rust
// Definido em std::fmt
pub trait Display {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
```

Ambos têm exatamente a mesma assinatura — a diferença está no **propósito** e no **marcador de formatação** que os invoca.

---

## Como Implementar: derive vs impl manual

### Debug com #[derive]

A forma mais comum de implementar `Debug` é usando a macro `derive`. O compilador gera automaticamente uma implementação que mostra o nome do tipo e todos os campos:

```rust
#[derive(Debug)]
struct Produto {
    nome: String,
    preco: f64,
    em_estoque: bool,
}

fn main() {
    let p = Produto {
        nome: String::from("Teclado Mecânico"),
        preco: 299.90,
        em_estoque: true,
    };

    // Saída: Produto { nome: "Teclado Mecânico", preco: 299.9, em_estoque: true }
    println!("{:?}", p);

    // Formatação alternativa (pretty-print):
    // Produto {
    //     nome: "Teclado Mecânico",
    //     preco: 299.9,
    //     em_estoque: true,
    // }
    println!("{:#?}", p);
}
```

Para usar `#[derive(Debug)]`, **todos os campos** do tipo também devem implementar `Debug`. Todos os tipos da biblioteca padrão já o fazem.

### Debug com implementação manual

Quando você quer controlar exatamente o que aparece na saída de depuração:

```rust
use std::fmt;

struct SenhaProtegida {
    usuario: String,
    senha: String,
}

impl fmt::Debug for SenhaProtegida {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SenhaProtegida")
            .field("usuario", &self.usuario)
            .field("senha", &"****")  // Nunca exibir a senha real
            .finish()
    }
}

fn main() {
    let cred = SenhaProtegida {
        usuario: String::from("admin"),
        senha: String::from("s3cr3t0"),
    };
    // Saída: SenhaProtegida { usuario: "admin", senha: "****" }
    println!("{:?}", cred);
}
```

### Display com implementação manual

`Display` **não pode** ser derivado automaticamente — você sempre precisa implementá-lo manualmente. Isso faz sentido, pois a representação amigável depende do contexto do seu domínio:

```rust
use std::fmt;

struct Moeda {
    valor: f64,
    simbolo: &'static str,
}

impl fmt::Display for Moeda {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {:.2}", self.simbolo, self.valor)
    }
}

fn main() {
    let preco = Moeda { valor: 49.9, simbolo: "R$" };
    // Saída: R$ 49.90
    println!("{}", preco);

    // Display também é usado por .to_string()
    let texto: String = preco.to_string();
    assert_eq!(texto, "R$ 49.90");
}
```

Implementar `Display` automaticamente fornece `.to_string()` graças a uma implementação blanket na biblioteca padrão: `impl<T: Display> ToString for T`.

---

## Exemplos Práticos

### Exemplo 1: Enum com Display e Debug

```rust
use std::fmt;

#[derive(Debug)]
enum StatusPedido {
    Pendente,
    Processando,
    Enviado { codigo_rastreio: String },
    Entregue,
    Cancelado(String), // motivo
}

impl fmt::Display for StatusPedido {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            StatusPedido::Pendente => write!(f, "Pendente"),
            StatusPedido::Processando => write!(f, "Em processamento"),
            StatusPedido::Enviado { codigo_rastreio } => {
                write!(f, "Enviado (rastreio: {})", codigo_rastreio)
            }
            StatusPedido::Entregue => write!(f, "Entregue"),
            StatusPedido::Cancelado(motivo) => {
                write!(f, "Cancelado: {}", motivo)
            }
        }
    }
}

fn main() {
    let status = StatusPedido::Enviado {
        codigo_rastreio: String::from("BR123456789"),
    };

    // Display: Enviado (rastreio: BR123456789)
    println!("Status: {}", status);

    // Debug: Enviado { codigo_rastreio: "BR123456789" }
    println!("Debug: {:?}", status);
}
```

### Exemplo 2: Usando f.write_str e o Formatter

```rust
use std::fmt;

struct Coordenada {
    lat: f64,
    lon: f64,
}

impl fmt::Display for Coordenada {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // f.write_str é mais eficiente que write! para strings fixas
        f.write_str("(")?;
        write!(f, "{:.4}, {:.4}", self.lat, self.lon)?;
        f.write_str(")")
    }
}

impl fmt::Debug for Coordenada {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Verifica se o modo alternativo {:#?} foi solicitado
        if f.alternate() {
            write!(
                f,
                "Coordenada {{\n  latitude: {},\n  longitude: {},\n}}",
                self.lat, self.lon
            )
        } else {
            write!(f, "Coordenada({}, {})", self.lat, self.lon)
        }
    }
}

fn main() {
    let local = Coordenada { lat: -23.5505, lon: -46.6333 };

    println!("{}", local);    // (-23.5505, -46.6333)
    println!("{:?}", local);  // Coordenada(-23.5505, -46.6333)
    println!("{:#?}", local);
    // Coordenada {
    //   latitude: -23.5505,
    //   longitude: -46.6333,
    // }
}
```

### Exemplo 3: Debug para coleções e tipos aninhados

```rust
#[derive(Debug)]
struct Aluno {
    nome: String,
    notas: Vec<f64>,
}

#[derive(Debug)]
struct Turma {
    disciplina: String,
    alunos: Vec<Aluno>,
}

fn main() {
    let turma = Turma {
        disciplina: String::from("Programação Rust"),
        alunos: vec![
            Aluno {
                nome: String::from("Ana"),
                notas: vec![9.5, 8.0, 10.0],
            },
            Aluno {
                nome: String::from("Bruno"),
                notas: vec![7.5, 8.5, 9.0],
            },
        ],
    };

    // {:#?} formata a estrutura aninhada de forma legível
    println!("{:#?}", turma);
}
```

### Exemplo 4: format! macro e formatação condicional

```rust
use std::fmt;

#[derive(Debug)]
struct Temperatura {
    celsius: f64,
}

impl fmt::Display for Temperatura {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Respeita a precisão solicitada pelo chamador
        if let Some(precisao) = f.precision() {
            write!(f, "{:.prec$}°C", self.celsius, prec = precisao)
        } else {
            write!(f, "{:.1}°C", self.celsius)
        }
    }
}

fn main() {
    let temp = Temperatura { celsius: 23.456 };

    // Usa a precisão padrão do Display (1 casa)
    println!("{}", temp);           // 23.5°C

    // Solicita 3 casas decimais
    println!("{:.3}", temp);        // 23.456°C

    // format! retorna uma String
    let msg = format!("A temperatura é {:.0}", temp);
    println!("{}", msg);            // A temperatura é 23°C
}
```

### Exemplo 5: Display para tipos wrapper (newtype)

```rust
use std::fmt;

struct Cpf([u8; 11]);

impl Cpf {
    fn new(digitos: [u8; 11]) -> Self {
        Cpf(digitos)
    }
}

impl fmt::Display for Cpf {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let d = &self.0;
        write!(
            f,
            "{}{}{}.{}{}{}.{}{}{}-{}{}",
            d[0], d[1], d[2], d[3], d[4], d[5],
            d[6], d[7], d[8], d[9], d[10]
        )
    }
}

impl fmt::Debug for Cpf {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Cpf(\"{}\")", self)  // Reutiliza Display
    }
}

fn main() {
    let cpf = Cpf::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]);
    println!("{}", cpf);   // 123.456.789-01
    println!("{:?}", cpf); // Cpf("123.456.789-01")
}
```

---

## Padrões e Boas Práticas

1. **Sempre derive `Debug`**: Adicione `#[derive(Debug)]` a todos os seus tipos. Isso facilita depuração, logs e mensagens de erro.

2. **Implemente `Display` quando faz sentido**: Nem todo tipo precisa de `Display`. Implemente-o quando seu tipo tem uma representação textual natural (como moeda, data, CPF, coordenadas).

3. **Oculte dados sensíveis**: Em implementações manuais de `Debug`, nunca exponha senhas, tokens ou dados pessoais. Use `"****"` ou `"[REDACTED]"`.

4. **Reutilize `Display` em `Debug`**: Se seu tipo tem um `Display` bom, sua implementação de `Debug` pode referenciá-lo com `write!(f, "NomeTipo(\"{}\")", self)`.

5. **Use `{:#?}` para depuração**: A formatação alternativa (`#`) produz saída indentada e multilinha, muito mais legível para estruturas complexas.

6. **`Display` é necessário para `Error`**: O trait `std::error::Error` exige que o tipo implemente `Display`. Veja [Error Trait](/stdlib/error-trait/) para mais detalhes.

7. **Prefira `write!` a concatenação**: Dentro de `fmt`, use `write!(f, ...)` em vez de construir strings intermediárias. Isso evita alocações desnecessárias.

---

## Veja Também

- [From e Into Traits](/stdlib/from-into/) — conversão de tipos, complementar a `Display`
- [Error Trait](/stdlib/error-trait/) — requer `Display` para mensagens de erro
- [Hash Trait](/stdlib/hash-trait/) — outro trait comumente derivado junto com `Debug`
- [Documentação de Código Rust](/artigos/documentacao-rust/) — como documentar seus tipos e traits
