---
title: "Tuplas em Rust: (T1, T2, ...)"
url: "https://rustlang.com.br/stdlib/tuplas/"
markdown_url: "https://rustlang.com.br/stdlib/tuplas.MD"
description: "Guia completo de tuplas em Rust: criação, desestruturação, unit type (), retorno de múltiplos valores, tuplas vs structs e exemplos práticos em português."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Tuplas em Rust: (T1, T2, ...)

Guia completo de tuplas em Rust: criação, desestruturação, unit type (), retorno de múltiplos valores, tuplas vs structs e exemplos práticos em português.


Uma **tupla** em Rust é um tipo composto de tamanho fixo que pode conter **valores de tipos diferentes**. Tuplas são escritas como `(T1, T2, T3, ...)` e são extremamente úteis para agrupar valores relacionados sem criar uma struct, retornar múltiplos valores de funções, e fazer desestruturação (pattern matching). O tipo unitário `()` (tupla vazia) é um caso especial fundamental na linguagem.

## Quando Usar Tuplas

Use tuplas quando:

- Precisa **agrupar temporariamente** valores de tipos diferentes.
- Quer **retornar múltiplos valores** de uma função.
- Precisa de **desestruturação** rápida em `let`, `match` ou parâmetros de closure.
- O significado dos campos é **óbvio pelo contexto** (ex.: coordenada `(x, y)`).

Se os campos precisam de **nomes descritivos** ou a tupla é usada em muitos lugares, prefira criar uma `struct`.

## Criando Tuplas

```rust
fn main() {
    // Tupla com tipos variados
    let pessoa: (&str, u32, f64) = ("Ana", 28, 1.65);

    // Tipo inferido
    let coordenada = (3.5, -2.1);

    // Tupla de um único elemento (note a vírgula!)
    let singleton = (42,);
    // Sem vírgula, (42) é apenas um inteiro entre parênteses

    // Tupla vazia — o tipo unitário ()
    let vazio: () = ();

    // Tupla aninhada
    let dados = ("Rust", (2015, 5, 15), true);

    // Tupla com elementos mutáveis
    let mut ponto = (0, 0);
    ponto.0 = 10;
    ponto.1 = 20;

    println!("Pessoa: {:?}", pessoa);
    println!("Coordenada: {:?}", coordenada);
    println!("Singleton: {:?}", singleton);
    println!("Ponto: {:?}", ponto);
    println!("Dados: {:?}", dados);
}
```

## Acessando Elementos e Desestruturação

```rust
fn main() {
    let rgb = (255u8, 128u8, 0u8);

    // Acesso por índice (começa em 0)
    println!("R: {}", rgb.0);
    println!("G: {}", rgb.1);
    println!("B: {}", rgb.2);

    // Desestruturação com let
    let (r, g, b) = rgb;
    println!("Cor: R={}, G={}, B={}", r, g, b);

    // Ignorar campos com _
    let (vermelho, _, _) = rgb;
    println!("Só o vermelho: {}", vermelho);

    // Desestruturação parcial com ..
    let tupla = (1, 2, 3, 4, 5);
    let (primeiro, ..) = tupla;
    let (.., ultimo) = tupla;
    let (primeiro2, .., ultimo2) = tupla;
    println!("Primeiro: {}, Último: {}", primeiro, ultimo);
    println!("Primeiro: {}, Último: {}", primeiro2, ultimo2);

    // Desestruturação em match
    let resultado = (true, 42);
    match resultado {
        (true, valor) => println!("Sucesso: {}", valor),
        (false, codigo) => println!("Erro: código {}", codigo),
    }
}
```

## Tabela de Características

| Característica | Descrição |
|---|---|
| Tamanho | Fixo em tempo de compilação |
| Tipos dos elementos | Podem ser diferentes |
| Máximo de elementos | 12 para a maioria dos traits (Debug, Clone, etc.) |
| Acesso | Por índice (`tupla.0`, `tupla.1`) ou desestruturação |
| Mutabilidade | Elementos mutáveis se a variável for `mut` |
| `Debug` | Implementado para tuplas de até 12 elementos |
| `PartialEq` / `Eq` | Implementado se todos os tipos implementam |
| `PartialOrd` / `Ord` | Implementado se todos os tipos implementam |
| `Clone` / `Copy` | Implementado se todos os tipos implementam |
| `Default` | Implementado se todos os tipos implementam |
| `Hash` | Implementado se todos os tipos implementam |
| Stack allocation | Sim (quando usado como variável local) |

## Exemplos Práticos

### 1. Retornando Múltiplos Valores de Funções

```rust
fn dividir(dividendo: f64, divisor: f64) -> (f64, f64) {
    let quociente = (dividendo / divisor).floor();
    let resto = dividendo % divisor;
    (quociente, resto)
}

fn min_max(valores: &[i32]) -> Option<(i32, i32)> {
    if valores.is_empty() {
        return None;
    }
    let mut min = valores[0];
    let mut max = valores[0];
    for &v in &valores[1..] {
        if v < min { min = v; }
        if v > max { max = v; }
    }
    Some((min, max))
}

fn analisar_texto(texto: &str) -> (usize, usize, usize) {
    let caracteres = texto.len();
    let palavras = texto.split_whitespace().count();
    let linhas = texto.lines().count();
    (caracteres, palavras, linhas)
}

fn main() {
    let (quociente, resto) = dividir(17.0, 5.0);
    println!("17 / 5 = {} resto {}", quociente, resto);

    let dados = [38, 27, 43, 3, 9, 82, 10];
    if let Some((min, max)) = min_max(&dados) {
        println!("Min: {}, Max: {}", min, max);
    }

    let texto = "Rust é uma linguagem\nde programação\nsegura e rápida";
    let (chars, palavras, linhas) = analisar_texto(texto);
    println!("Caracteres: {}, Palavras: {}, Linhas: {}", chars, palavras, linhas);
}
```

### 2. Tuplas em Iteradores e Closures

```rust
fn main() {
    let nomes = vec!["Ana", "Bruno", "Carla"];
    let idades = vec![28, 35, 22];

    // zip cria tuplas
    let pessoas: Vec<(&str, i32)> = nomes.iter()
        .copied()
        .zip(idades.iter().copied())
        .collect();
    println!("Pessoas: {:?}", pessoas);

    // enumerate retorna tuplas (índice, valor)
    for (i, nome) in nomes.iter().enumerate() {
        println!("{}. {}", i + 1, nome);
    }

    // Desestruturação em closures
    let mais_velha = pessoas.iter()
        .max_by_key(|&(_, idade)| idade)
        .unwrap();
    println!("Mais velha: {} ({})", mais_velha.0, mais_velha.1);

    // Ordenar por segundo elemento da tupla
    let mut dados = vec![("C", 3), ("A", 1), ("B", 2)];
    dados.sort_by_key(|&(_, v)| v);
    println!("Ordenado: {:?}", dados); // [("A", 1), ("B", 2), ("C", 3)]
}
```

### 3. O Tipo Unitário () e Funções Sem Retorno

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

// Funções sem retorno explícito retornam ()
fn saudar(nome: &str) {
    println!("Olá, {}!", nome);
}

// Equivalente explícito
fn saudar_explicito(nome: &str) -> () {
    println!("Olá, {}!", nome);
}

fn main() {
    // () é um tipo real com exatamente um valor: ()
    let resultado: () = saudar("Mundo");
    println!("Tipo de resultado: {:?}", resultado); // ()

    // () em genéricos: HashMap<K, ()> é basicamente um HashSet
    let mut conjunto: HashSet<&str> = HashSet::new();
    conjunto.insert("item");

    // () como "nenhum dado" em enums
    enum Evento {
        Clique(i32, i32),     // com dados
        Tecla(char),           // com dados
        Fechar,                // sem dados (implicitamente ())
    }

    // Result<(), Error> — operação que pode falhar mas não retorna valor
    fn salvar_arquivo(conteudo: &str) -> Result<(), String> {
        if conteudo.is_empty() {
            Err("Conteúdo vazio".to_string())
        } else {
            println!("Salvando: {}...", &conteudo[..20.min(conteudo.len())]);
            Ok(())
        }
    }

    match salvar_arquivo("dados importantes") {
        Ok(()) => println!("Salvo com sucesso!"),
        Err(e) => println!("Erro: {}", e),
    }
}
```

### 4. Tuplas vs Structs — Quando Usar Cada Um

```rust
// Tupla: bom para valores temporários e óbvios
fn coordenadas_mouse() -> (i32, i32) {
    (100, 200) // x, y — óbvio pelo contexto
}

// Struct: melhor quando os campos precisam de nomes
#[derive(Debug)]
struct Retangulo {
    largura: f64,
    altura: f64,
}

// Tuple struct: meio-termo — tipo nomeado com campos posicionais
#[derive(Debug)]
struct Cor(u8, u8, u8);

#[derive(Debug)]
struct Metros(f64);

#[derive(Debug)]
struct Quilometros(f64);

fn main() {
    let (x, y) = coordenadas_mouse();
    println!("Mouse em ({}, {})", x, y);

    let ret = Retangulo { largura: 10.0, altura: 5.0 };
    println!("Retângulo: {:?}", ret);

    // Tuple structs dão type safety
    let vermelho = Cor(255, 0, 0);
    println!("Vermelho: {:?}", vermelho);

    let distancia = Metros(1500.0);
    let outra = Quilometros(1.5);
    // Metros e Quilometros são tipos diferentes!
    // Não podemos comparar ou misturá-los acidentalmente
    println!("{:?} e {:?}", distancia, outra);
}
```

### 5. Padrões Avançados com Tuplas

```rust
fn main() {
    // Swap de variáveis com tuplas
    let mut a = 10;
    let mut b = 20;
    (a, b) = (b, a); // swap!
    println!("a={}, b={}", a, b); // a=20, b=10

    // Pattern matching complexo
    let resultados: Vec<(bool, i32)> = vec![
        (true, 100),
        (false, -1),
        (true, 200),
        (false, -2),
        (true, 150),
    ];

    let (sucessos, falhas): (Vec<_>, Vec<_>) = resultados
        .iter()
        .partition(|&&(ok, _)| ok);

    println!("Sucessos: {:?}", sucessos);
    println!("Falhas: {:?}", falhas);

    // Tuplas como chaves de HashMap
    use std::collections::HashMap;
    let mut grid: HashMap<(i32, i32), &str> = HashMap::new();
    grid.insert((0, 0), "origem");
    grid.insert((1, 0), "leste");
    grid.insert((0, 1), "norte");

    for ((x, y), nome) in &grid {
        println!("({}, {}) = {}", x, y, nome);
    }

    // Comparação lexicográfica de tuplas
    // Compara primeiro elemento, depois segundo, etc.
    assert!((1, 2) < (1, 3));
    assert!((1, 2) < (2, 0));
    assert!((1, 2, 3) == (1, 2, 3));
    println!("Comparações de tuplas OK!");
}
```

## Características de Desempenho

As tuplas têm **zero overhead em tempo de execução**. O compilador sabe exatamente o layout de memória e gera código otimizado.

| Aspecto | Tupla | Struct | Vec (de tuplas) |
|---|---|---|---|
| Alocação | Stack | Stack | Heap |
| Overhead | 0 bytes | 0 bytes | 24 bytes |
| Acesso a campo | O(1) | O(1) | O(1) |
| Layout | Compilador decide | Compilador decide | Contíguo |
| Tamanho | `sum(size_of campos)` + padding | Igual | Dinâmico |

**Padding:** O compilador pode inserir bytes de padding entre campos de uma tupla para alinhar os dados na memória. O layout exato não é garantido (a menos que use `#[repr(C)]` em uma struct equivalente).

**Comparação:** Tuplas implementam comparação **lexicográfica** — compara o primeiro elemento, depois o segundo em caso de empate, e assim por diante. Isso é útil para ordenação multi-critério:

```rust
fn main() {
    let mut alunos = vec![
        ("Ana", 9.5),
        ("Bruno", 8.0),
        ("Carla", 9.5),
        ("Daniel", 7.0),
    ];

    // Ordena por nota (decrescente), depois por nome (crescente)
    alunos.sort_by(|a, b| {
        b.1.partial_cmp(&a.1)
            .unwrap()
            .then(a.0.cmp(&b.0))
    });

    for (nome, nota) in &alunos {
        println!("{}: {}", nome, nota);
    }
    // Ana: 9.5
    // Carla: 9.5
    // Bruno: 8.0
    // Daniel: 7.0
}
```

## Limitações das Tuplas

- **Máximo de 12 elementos** para traits derivados automaticamente (`Debug`, `Clone`, etc.). Tuplas maiores podem existir, mas não terão esses traits.
- **Sem nomes de campos** — o significado de `tupla.3` pode não ser óbvio. Use structs para dados complexos.
- **Sem iteração genérica** — não existe `.iter()` para tuplas (os elementos podem ter tipos diferentes).
- **Tipos rígidos** — `(i32, String)` e `(String, i32)` são tipos completamente diferentes.

## Veja Também

- [Arrays em Rust](/stdlib/arrays/) --- coleção de tamanho fixo com tipos homogêneos
- [Structs, Enums e Pattern Matching](/tutoriais/structs-enums-pattern-matching/) --- quando usar structs em vez de tuplas
- [Variáveis, Tipos e Funções](/tutoriais/variaveis-tipos-funcoes/) --- fundamentos dos tipos em Rust
- [HashMap em Rust](/stdlib/hashmap/) --- usando tuplas como chaves de mapas
- [Ranges em Rust](/stdlib/ranges/) --- faixas para iteração
- [Tipos Numéricos](/stdlib/tipos-numericos/) --- os tipos que frequentemente compõem tuplas
