---
title: "Structs, Enums e Pattern Matching em Rust"
url: "https://rustlang.com.br/tutoriais/structs-enums-pattern-matching/"
markdown_url: "https://rustlang.com.br/tutoriais/structs-enums-pattern-matching.MD"
description: "Aprenda a modelar dados com structs e enums, criar métodos com blocos impl e usar pattern matching com match, if let e while let."
date: "2026-02-21"
author: "Equipe Rust Brasil"
---

# Structs, Enums e Pattern Matching em Rust

Aprenda a modelar dados com structs e enums, criar métodos com blocos impl e usar pattern matching com match, if let e while let.


Structs e enums são as ferramentas fundamentais para modelar dados em Rust. Combinados com pattern matching, eles permitem escrever código expressivo, seguro e elegante. Neste tutorial, vamos explorar cada um desses conceitos em profundidade.

## Structs (Estruturas)

Structs permitem agrupar dados relacionados em um único tipo. Existem três tipos de structs em Rust.

### Struct Regular (Named Fields)

A forma mais comum, com campos nomeados:

```rust
struct Usuario {
    nome: String,
    email: String,
    idade: u32,
    ativo: bool,
}

fn main() {
    // Criando uma instância
    let usuario = Usuario {
        nome: String::from("Maria Silva"),
        email: String::from("maria@exemplo.com"),
        idade: 28,
        ativo: true,
    };

    println!("Nome: {}", usuario.nome);
    println!("Email: {}", usuario.email);
    println!("Idade: {}", usuario.idade);
    println!("Ativo: {}", usuario.ativo);
}
```

### Struct Mutável

Para modificar campos, a variável inteira deve ser `mut` (Rust não permite mutabilidade por campo):

```rust
struct Usuario {
    nome: String,
    email: String,
    idade: u32,
    ativo: bool,
}

fn main() {
    let mut usuario = Usuario {
        nome: String::from("Carlos"),
        email: String::from("carlos@exemplo.com"),
        idade: 30,
        ativo: true,
    };

    usuario.email = String::from("carlos.novo@exemplo.com");
    usuario.idade = 31;
    println!("{} - {}", usuario.nome, usuario.email);
}
```

### Field Init Shorthand e Struct Update Syntax

```rust
struct Usuario {
    nome: String,
    email: String,
    idade: u32,
    ativo: bool,
}

fn criar_usuario(nome: String, email: String) -> Usuario {
    Usuario {
        nome,       // shorthand: 'nome: nome' vira apenas 'nome'
        email,      // shorthand: 'email: email' vira apenas 'email'
        idade: 0,
        ativo: true,
    }
}

fn main() {
    let usuario1 = criar_usuario(
        String::from("Ana"),
        String::from("ana@exemplo.com"),
    );

    // Struct update syntax: cria um novo a partir de um existente
    let usuario2 = Usuario {
        nome: String::from("Bruno"),
        email: String::from("bruno@exemplo.com"),
        ..usuario1  // usa os campos restantes de usuario1
    };

    // Cuidado: usuario1.nome e usuario1.email foram movidos para usuario2!
    // Mas usuario1.idade e usuario1.ativo foram copiados (implementam Copy)
    println!("{} - idade: {}", usuario2.nome, usuario2.idade);
}
```

### Tuple Structs

Structs sem nomes nos campos, úteis para criar tipos distintos a partir de tuplas:

```rust
struct Cor(u8, u8, u8);
struct Ponto(f64, f64);
struct Metros(f64);
struct Quilometros(f64);

fn main() {
    let vermelho = Cor(255, 0, 0);
    let origem = Ponto(0.0, 0.0);
    let distancia = Metros(100.0);

    println!("R: {}, G: {}, B: {}", vermelho.0, vermelho.1, vermelho.2);
    println!("Ponto: ({}, {})", origem.0, origem.1);
    println!("Distância: {}m", distancia.0);

    // Tipo seguro: não pode misturar Metros com Quilometros
    let _km = Quilometros(5.0);
    // let soma = distancia.0 + _km.0; // Funciona, mas semanticamente errado
    // Structs diferentes = tipos diferentes no compilador
}
```

### Unit Structs

Structs sem campos, úteis para implementar traits:

```rust
struct Vazio;

fn main() {
    let _v = Vazio;
    // Unit structs são úteis quando você precisa implementar
    // um trait mas não tem dados para armazenar
}
```

## Blocos impl: Métodos e Funções Associadas

O bloco `impl` permite adicionar métodos e funções associadas a structs:

```rust
struct Retangulo {
    largura: f64,
    altura: f64,
}

impl Retangulo {
    // Função associada (como "static method" em outras linguagens)
    // Não recebe &self — chamada com Retangulo::novo()
    fn novo(largura: f64, altura: f64) -> Self {
        Self { largura, altura }
    }

    fn quadrado(lado: f64) -> Self {
        Self {
            largura: lado,
            altura: lado,
        }
    }

    // Método: recebe &self (referência imutável)
    fn area(&self) -> f64 {
        self.largura * self.altura
    }

    fn perimetro(&self) -> f64 {
        2.0 * (self.largura + self.altura)
    }

    fn eh_quadrado(&self) -> bool {
        (self.largura - self.altura).abs() < f64::EPSILON
    }

    // Método que recebe &mut self (pode modificar)
    fn redimensionar(&mut self, fator: f64) {
        self.largura *= fator;
        self.altura *= fator;
    }

    // Método que consome self (toma ownership)
    fn dividir(self) -> (Retangulo, Retangulo) {
        let metade_largura = self.largura / 2.0;
        (
            Retangulo::novo(metade_largura, self.altura),
            Retangulo::novo(metade_largura, self.altura),
        )
    }
}

fn main() {
    // Usando funções associadas
    let mut ret = Retangulo::novo(10.0, 5.0);
    let quad = Retangulo::quadrado(7.0);

    println!("Retângulo: {}x{}", ret.largura, ret.altura);
    println!("Área: {}", ret.area());
    println!("Perímetro: {}", ret.perimetro());
    println!("É quadrado? {}", ret.eh_quadrado());

    println!("\nQuadrado: {}x{}", quad.largura, quad.altura);
    println!("É quadrado? {}", quad.eh_quadrado());

    // Método mutável
    ret.redimensionar(2.0);
    println!("\nApós redimensionar: {}x{}", ret.largura, ret.altura);
    println!("Nova área: {}", ret.area());

    // Método que consome ownership
    let (esq, dir) = ret.dividir();
    // ret não é mais válido aqui!
    println!("\nDividido: {}x{} e {}x{}", esq.largura, esq.altura, dir.largura, dir.altura);
}
```

Os três tipos de `self`:
- **`&self`** — Empresta a struct (leitura apenas, mais comum)
- **`&mut self`** — Empresta mutavelmente (pode modificar)
- **`self`** — Toma ownership (consome a struct)

## Exibindo Structs com Debug e Display

```rust
// Derive Debug para impressão de depuração
#[derive(Debug)]
struct Produto {
    nome: String,
    preco: f64,
    estoque: u32,
}

impl std::fmt::Display for Produto {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} - R$ {:.2} ({} em estoque)", self.nome, self.preco, self.estoque)
    }
}

fn main() {
    let produto = Produto {
        nome: String::from("Teclado Mecânico"),
        preco: 299.90,
        estoque: 15,
    };

    println!("Debug: {:?}", produto);    // usa Debug
    println!("Display: {}", produto);     // usa Display
    println!("Pretty: {:#?}", produto);   // Debug formatado
}
```

## Enums (Enumerações)

Enums representam tipos que podem ser **uma entre várias variantes**. Em Rust, cada variante pode carregar dados:

### Enum Simples

```rust
enum DiaDaSemana {
    Segunda,
    Terca,
    Quarta,
    Quinta,
    Sexta,
    Sabado,
    Domingo,
}

fn tipo_do_dia(dia: &DiaDaSemana) -> &str {
    match dia {
        DiaDaSemana::Sabado | DiaDaSemana::Domingo => "Fim de semana",
        _ => "Dia útil",
    }
}

fn main() {
    let hoje = DiaDaSemana::Quarta;
    println!("Hoje é: {}", tipo_do_dia(&hoje));
}
```

### Enum com Dados

Esta é uma das funcionalidades mais poderosas do Rust — cada variante pode ter tipos de dados diferentes:

```rust
#[derive(Debug)]
enum Forma {
    Circulo(f64),                      // raio
    Retangulo(f64, f64),               // largura, altura
    Triangulo { base: f64, altura: f64 }, // campos nomeados
    Ponto,                              // sem dados
}

impl Forma {
    fn area(&self) -> f64 {
        match self {
            Forma::Circulo(raio) => std::f64::consts::PI * raio * raio,
            Forma::Retangulo(largura, altura) => largura * altura,
            Forma::Triangulo { base, altura } => base * altura / 2.0,
            Forma::Ponto => 0.0,
        }
    }

    fn descricao(&self) -> String {
        match self {
            Forma::Circulo(r) => format!("Círculo com raio {:.1}", r),
            Forma::Retangulo(l, a) => format!("Retângulo {}x{}", l, a),
            Forma::Triangulo { base, altura } => {
                format!("Triângulo (base: {}, altura: {})", base, altura)
            }
            Forma::Ponto => String::from("Ponto"),
        }
    }
}

fn main() {
    let formas: Vec<Forma> = vec![
        Forma::Circulo(5.0),
        Forma::Retangulo(10.0, 3.0),
        Forma::Triangulo { base: 8.0, altura: 4.0 },
        Forma::Ponto,
    ];

    for forma in &formas {
        println!("{}: área = {:.2}", forma.descricao(), forma.area());
    }
}
```

Saída:

```
Círculo com raio 5.0: área = 78.54
Retângulo 10x3: área = 30.00
Triângulo (base: 8, altura: 4): área = 16.00
Ponto: área = 0.00
```

### Option e Result: Enums da Biblioteca Padrão

Os dois enums mais importantes do Rust:

```rust
// Option<T> - representa um valor que pode ou não existir
// enum Option<T> {
//     Some(T),
//     None,
// }

fn buscar_usuario(id: u32) -> Option<String> {
    match id {
        1 => Some(String::from("Maria")),
        2 => Some(String::from("João")),
        _ => None,
    }
}

fn main() {
    // Option
    let usuario = buscar_usuario(1);
    match usuario {
        Some(nome) => println!("Encontrado: {}", nome),
        None => println!("Usuário não encontrado"),
    }

    let desconhecido = buscar_usuario(99);
    match desconhecido {
        Some(nome) => println!("Encontrado: {}", nome),
        None => println!("Usuário não encontrado"),
    }
}
```

## Pattern Matching com match

O `match` é uma das construções mais poderosas do Rust. Ele verifica **exaustivamente** todas as possibilidades:

```rust
fn classificar_numero(n: i32) -> &'static str {
    match n {
        0 => "zero",
        1..=9 => "um dígito positivo",
        10..=99 => "dois dígitos",
        100..=999 => "três dígitos",
        n if n < 0 => "negativo",
        _ => "mil ou mais",
    }
}

fn main() {
    let numeros = [0, 5, 42, 500, -3, 1000];
    for n in numeros {
        println!("{}: {}", n, classificar_numero(n));
    }
}
```

### Patterns Avançados

```rust
#[derive(Debug)]
enum Comando {
    Sair,
    Mover { x: i32, y: i32 },
    Escrever(String),
    MudarCor(u8, u8, u8),
}

fn executar(comando: &Comando) {
    match comando {
        Comando::Sair => {
            println!("Saindo do programa...");
        }
        Comando::Mover { x, y } => {
            println!("Movendo para ({}, {})", x, y);
        }
        Comando::Escrever(texto) => {
            println!("Escrevendo: {}", texto);
        }
        Comando::MudarCor(r, g, b) => {
            println!("Cor: RGB({}, {}, {})", r, g, b);
        }
    }
}

fn main() {
    let comandos = vec![
        Comando::Mover { x: 10, y: 20 },
        Comando::Escrever(String::from("Olá!")),
        Comando::MudarCor(255, 128, 0),
        Comando::Sair,
    ];

    for cmd in &comandos {
        executar(cmd);
    }
}
```

### Desestruturação em match

```rust
fn main() {
    // Desestruturar tuplas
    let ponto = (3, -5);
    match ponto {
        (0, 0) => println!("Na origem"),
        (x, 0) => println!("No eixo X em {}", x),
        (0, y) => println!("No eixo Y em {}", y),
        (x, y) if x > 0 && y > 0 => println!("Primeiro quadrante: ({}, {})", x, y),
        (x, y) => println!("Em ({}, {})", x, y),
    }

    // Match com referências
    let texto = String::from("Rust");
    match texto.as_str() {
        "Rust" => println!("Linguagem de sistemas!"),
        "Python" => println!("Linguagem de scripting!"),
        outro => println!("Linguagem: {}", outro),
    }

    // Binding com @
    let idade = 25;
    match idade {
        n @ 0..=12 => println!("Criança de {} anos", n),
        n @ 13..=17 => println!("Adolescente de {} anos", n),
        n @ 18..=64 => println!("Adulto de {} anos", n),
        n => println!("Idoso de {} anos", n),
    }
}
```

## if let e while let

Quando você só se importa com **uma** variante, `if let` é mais conciso que `match`:

### if let

```rust
fn main() {
    let valor: Option<i32> = Some(42);

    // Com match
    match valor {
        Some(v) => println!("Valor: {}", v),
        None => {}
    }

    // Com if let (mais conciso!)
    if let Some(v) = valor {
        println!("Valor: {}", v);
    }

    // if let com else
    let resultado: Result<String, String> = Err(String::from("Arquivo não encontrado"));
    if let Ok(conteudo) = resultado {
        println!("Conteúdo: {}", conteudo);
    } else {
        println!("Não foi possível ler o arquivo");
    }

    // Encadeando if let
    let config_porta: Option<u16> = Some(8080);
    let config_host: Option<String> = Some(String::from("localhost"));

    if let (Some(porta), Some(host)) = (config_porta, &config_host) {
        println!("Servidor: {}:{}", host, porta);
    }
}
```

### while let

Útil para iterar enquanto um padrão é satisfeito:

```rust
fn main() {
    let mut pilha = vec![1, 2, 3, 4, 5];

    // pop() retorna Option<T>
    while let Some(topo) = pilha.pop() {
        println!("Desempilhou: {}", topo);
    }

    println!("Pilha vazia!");
}
```

Saída:

```
Desempilhou: 5
Desempilhou: 4
Desempilhou: 3
Desempilhou: 2
Desempilhou: 1
Pilha vazia!
```

## Exemplo Prático: Sistema de Pedidos

Vamos combinar tudo em um exemplo prático:

```rust
#[derive(Debug)]
struct Endereco {
    rua: String,
    cidade: String,
    estado: String,
}

#[derive(Debug)]
struct Cliente {
    nome: String,
    email: String,
}

#[derive(Debug)]
enum MetodoPagamento {
    CartaoCredito { numero: String, bandeira: String },
    Pix(String),          // chave PIX
    Boleto,
}

#[derive(Debug)]
enum StatusPedido {
    Pendente,
    Pago,
    Enviado(String),      // código de rastreamento
    Entregue,
    Cancelado(String),    // motivo
}

#[derive(Debug)]
struct ItemPedido {
    nome: String,
    preco: f64,
    quantidade: u32,
}

#[derive(Debug)]
struct Pedido {
    id: u32,
    cliente: Cliente,
    endereco: Endereco,
    itens: Vec<ItemPedido>,
    pagamento: MetodoPagamento,
    status: StatusPedido,
}

impl ItemPedido {
    fn subtotal(&self) -> f64 {
        self.preco * self.quantidade as f64
    }
}

impl Pedido {
    fn total(&self) -> f64 {
        self.itens.iter().map(|item| item.subtotal()).sum()
    }

    fn resumo_pagamento(&self) -> String {
        match &self.pagamento {
            MetodoPagamento::CartaoCredito { numero, bandeira } => {
                let ultimos4 = &numero[numero.len() - 4..];
                format!("Cartão {} final {}", bandeira, ultimos4)
            }
            MetodoPagamento::Pix(chave) => {
                format!("PIX (chave: {})", chave)
            }
            MetodoPagamento::Boleto => {
                String::from("Boleto bancário")
            }
        }
    }

    fn resumo_status(&self) -> String {
        match &self.status {
            StatusPedido::Pendente => String::from("Aguardando pagamento"),
            StatusPedido::Pago => String::from("Pagamento confirmado"),
            StatusPedido::Enviado(codigo) => {
                format!("Enviado - Rastreio: {}", codigo)
            }
            StatusPedido::Entregue => String::from("Entregue com sucesso"),
            StatusPedido::Cancelado(motivo) => {
                format!("Cancelado: {}", motivo)
            }
        }
    }
}

fn main() {
    let pedido = Pedido {
        id: 1001,
        cliente: Cliente {
            nome: String::from("Ana Costa"),
            email: String::from("ana@exemplo.com"),
        },
        endereco: Endereco {
            rua: String::from("Rua das Flores, 123"),
            cidade: String::from("São Paulo"),
            estado: String::from("SP"),
        },
        itens: vec![
            ItemPedido {
                nome: String::from("Livro: Programming Rust"),
                preco: 89.90,
                quantidade: 1,
            },
            ItemPedido {
                nome: String::from("Adesivo Ferris"),
                preco: 5.00,
                quantidade: 3,
            },
        ],
        pagamento: MetodoPagamento::CartaoCredito {
            numero: String::from("4111111111111234"),
            bandeira: String::from("Visa"),
        },
        status: StatusPedido::Enviado(String::from("BR123456789")),
    };

    println!("=== Pedido #{} ===", pedido.id);
    println!("Cliente: {}", pedido.cliente.nome);
    println!(
        "Endereço: {}, {} - {}",
        pedido.endereco.rua, pedido.endereco.cidade, pedido.endereco.estado
    );

    println!("\nItens:");
    for item in &pedido.itens {
        println!(
            "  {} ({}x R$ {:.2}) = R$ {:.2}",
            item.nome, item.quantidade, item.preco, item.subtotal()
        );
    }

    println!("\nTotal: R$ {:.2}", pedido.total());
    println!("Pagamento: {}", pedido.resumo_pagamento());
    println!("Status: {}", pedido.resumo_status());

    // Usando if let para verificar status específico
    if let StatusPedido::Enviado(codigo) = &pedido.status {
        println!("\nRastreie seu pedido: https://rastreamento.exemplo.com/{}", codigo);
    }
}
```

Saída:

```
=== Pedido #1001 ===
Cliente: Ana Costa
Endereço: Rua das Flores, 123, São Paulo - SP

Itens:
  Livro: Programming Rust (1x R$ 89.90) = R$ 89.90
  Adesivo Ferris (3x R$ 5.00) = R$ 15.00

Total: R$ 104.90
Pagamento: Cartão Visa final 1234
Status: Enviado - Rastreio: BR123456789

Rastreie seu pedido: https://rastreamento.exemplo.com/BR123456789
```

## Próximos Passos

Com structs, enums e pattern matching, você já tem as ferramentas para modelar domínios complexos de forma segura e expressiva. No próximo tutorial, vamos aprender como lidar com erros de forma elegante em Rust.

Acesse o tutorial [Tratamento de Erros](/tutoriais/tratamento-de-erros/) para continuar.

---

Outras linguagens oferecem recursos similares para modelagem de dados — compare:

- <a href="https://kotlin.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">Data classes e sealed classes em Kotlin</a> — o equivalente Kotlin para structs e enums com pattern matching
- <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Dataclasses em Python</a> — decorador @dataclass e structural pattern matching do Python 3.10+
