Rust: Matrizes e tuplas em Rust

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

Matrizes e tuplas são as “coleções de tamanho fixo” mais básicas em Rust — as matrizes armazenam vários valores do mesmo tipo, enquanto as tuplas podem armazenar vários valores de tipos diferentes. Ambas são alocadas na pilha e não acarretam sobrecarga de tempo de execução.

Na programação de sistemas, coleções de tamanho fixo significam um layout de memória bem definido e um desempenho previsível. O compilador do Rust precisa saber quanta memória cada variável ocupa, e os tamanhos das matrizes e tuplas são totalmente determinados em tempo de compilação.


1. O que você vai aprender


2. A história de um analista financeiro

(1) O problema: armazenamento de dados em variáveis diferentes

Alice é analista financeira na empresa e precisa armazenar os dados de receita mensal do primeiro semestre de 2026:

RUST
let jan = 120_000;  // Jan
let feb = 135_000;  // Feb
let mar = 128_000;  // Mar
let apr = 142_000;  // Apr
let may = 150_000;  // May
let jun = 165_000;  // Jun

// Want to calculate half-year total?
let sum = jan + feb + mar + apr + may + jun;  // Tedious and error-prone

Dados correspondentes a seis meses já são um saco — e se você tivesse que armazenar dados de 365 dias? Você teria que definir 365 variáveis? Passar seis variáveis para uma função já é o suficiente para te deixar louco. Além disso, não dá para usar um loop para lidar com elas.

(2) Solução para matrizes em Rust

RUST
fn main() {
    // Half-year revenue: Jan ~ Jun 2026
    let revenue: [i32; 6] = [120_000, 135_000, 128_000, 142_000, 150_000, 165_000];

    // Total: one line
    let sum: i32 = revenue.iter().sum();
    println!("2026 H1 total revenue: {} yuan", sum);

    // Average
    let avg = sum as f64 / revenue.len() as f64;
    println!("Monthly average: {:.0} yuan", avg);

    // Print each month
    for (i, val) in revenue.iter().enumerate() {
        println!("Month {}: {} yuan", i + 1, val);
    }
}

As matrizes armazenam todos os dados do mesmo tipo em uma única variável — é possível acessá-los por meio de índices, percorrê-los usando loops, passá-los para funções e usar iteradores para calcular e agregar dados. Seis linhas de código substituem 20 linhas de variáveis espalhadas.


3. Matrizes e tuplas

(1) Comparação de conceitos

100%
graph TB
    A[Rust fixed-size collections] --> B[Array [T; N]]
    A --> C[Tuple (T1, T2, ...)]
    B --> D[All elements same type]
    B --> E[Compile-time fixed length]
    B --> F[Index access: arr[i]]
    C --> G[Elements can differ in type]
    C --> H[Pattern destructuring]
    C --> I[Multiple return values]

(2) Matrizes x Tuplas

Dimensão Matriz [T; N] Tupla (T1, T2, ...)
Tipo de elemento Todos devem ser iguais Podem ser diferentes
Comprimento Fixo na compilação (N) Fixo na compilação (número de elementos)
Método de acesso arr[index] tuple.field_index ou Desconstrução
Casos de uso Conjuntos de dados do mesmo tipo (por exemplo, receita mensal) Combinações de dados heterogêneos (por exemplo, valores de retorno e códigos de erro)
Memória Bloco de memória contíguo Bloco de memória contíguo (pode incluir preenchimento de alinhamento)
Parâmetros genéricos [T; N] — Tipo + Comprimento (T1, T2) — Tipo em cada posição
Verificação de ultrapassagem de limites Pânico em tempo de execução N/A (o número do campo é conhecido em tempo de compilação)

(3) Referência rápida aos métodos comuns de matrizes

Método Tipo de retorno Descrição
len() usize Retornar o comprimento da matriz
get(i) Option<&T> Acesso seguro ao índice
get_mut(i) Option<&mut T> Acesso seguro a variáveis
iter() Iter<T> Voltar para Iteradores de referência
contains(&val) bool Se um valor está incluído
sort() () Classificação no próprio local (requer &mut)
map(f) Convertido por meio de .iter().map()
reverse() () Rotação no próprio lugar (requer &mut)

(4) Seleção de um conjunto de comprimento fixo

Cenário Tipo recomendado Motivo
Número fixo de elementos do mesmo tipo [T; N] Matriz Alocação na pilha, sem sobrecarga, segura quanto ao tipo
Combinações fixas heterogêneas (T1, T2, ...) Tupla Combinações de tipos diferentes, desconstrução de padrões
Funções com vários valores de retorno Tuplas Leve; não é necessário definir estruturas
RGB/Coordenadas Estrutura de tupla Possui um nome de tipo, evita colisões de nomes
Grandes quantidades de dados semelhantes Vec<T> Crescimento dinâmico, alocação em heap

4. Exemplos de matrizes e tuplas

▶ Exemplo 1: Declaração, acesso e iteração de matrizes (Dificuldade ⭐)

RUST
// ============================================
// Arrays: declaration, indexing, and iteration
// ============================================

fn main() {
    // Type 1: Explicit type annotation
    let months: [i32; 6] = [100, 200, 300, 400, 500, 600];

    // Type 2: Type inference
    let zeros = [0; 5];  // [0, 0, 0, 0, 0], shorthand for [0, 0, 0, 0, 0]

    // Access by index (0-based)
    println!("First month: {}", months[0]);   // 100
    println!("Third month: {}", months[2]);   // 300

    // len() returns the array length
    println!("Array length: {}", months.len());  // 6

    // Iterate with a for loop
    print!("All values: ");
    for val in months {
        print!("{} ", val);
    }
    println!();

    // Iterate with index using .iter().enumerate()
    for (index, value) in months.iter().enumerate() {
        println!("months[{}] = {}", index, value);
    }

    // The shorthand `[val; N]` syntax
    println!("Zeros array: {:?}", zeros);
}

Resultado:

TEXT 📖 Somente leitura
First month: 100
Third month: 300
Array length: 6
All values: 100 200 300 400 500 600
months[0] = 100
months[1] = 200
months[2] = 300
months[3] = 400
months[4] = 500
months[5] = 600
Zeros array: [0, 0, 0, 0, 0]

Existem duas maneiras de declarar um array: [initial_value; length] é uma forma abreviada e prática, e [type; length] é a sintaxe completa. Use arr[i] para acessar elementos por índice e for val in arr para percorrer o array. .iter().enumerate() permite recuperar o índice e o valor ao mesmo tempo.


▶ Exemplo 2: Índice de matriz fora dos limites e verificações de segurança (Dificuldade ⭐⭐)

RUST
// ============================================
// Array out-of-bounds: Rust panics at runtime
// ============================================

fn main() {
    let scores: [i32; 3] = [95, 87, 92];

    // Safe access: within bounds
    println!("scores[0] = {}", scores[0]);  // OK
    println!("scores[1] = {}", scores[1]);  // OK
    println!("scores[2] = {}", scores[2]);  // OK

    // Out of bounds: THIS WILL PANIC at runtime
    // Uncomment the line below to see the error:
    // println!("scores[5] = {}", scores[5]);
    //
    // Output:
    // thread 'main' panicked at src/main.rs:XX:YY:
    // index out of bounds: the len is 3 but the index is 5

    // Safe alternative: use .get() which returns Option<&T>
    let first = scores.get(0);   // Some(&95)
    let invalid = scores.get(5); // None

    match first {
        Some(val) => println!("Safe get(0): {}", val),
        None => println!("Index 0 out of bounds"),
    }

    match invalid {
        Some(val) => println!("Safe get(5): {}", val),
        None => println!("Index 5 out of bounds -- safely handled!"),
    }

    // Using .get() with a default value
    let val = scores.get(5).copied().unwrap_or(-1);
    println!("scores.get(5) with default: {}", val);  // -1
}

Resultado:

TEXT 📖 Somente leitura
scores[0] = 95
scores[1] = 87
scores[2] = 92
Safe get(0): 95
Index 5 out of bounds -- safely handled!
scores.get(5) with default: -1

Usar arr[i] diretamente causará um panic em tempo de execução (trava) se o índice estiver fora dos limites. A maneira segura é usar o método .get() — ele retorna Option<&T>, permitindo que você lide de forma adequada com condições fora dos limites usando match ou unwrap_or, em vez de causar uma trava imediata.


▶ Exemplo 3: Criação e desempacotamento de tuplas e valores de retorno de funções (Dificuldade ⭐⭐)

RUST
// ============================================
// Tuples: creation, destructuring, and return values
// ============================================

// A function that returns a tuple: (sum, count, average)
fn analyze_sales(sales: &[i32]) -> (i32, usize, f64) {
    let sum: i32 = sales.iter().sum();
    let count = sales.len();
    let avg = sum as f64 / count as f64;
    (sum, count, avg)  // return as a tuple
}

fn main() {
    // Tuple with different types: (name, age, active)
    let person: (&str, u8, bool) = ("Alice", 30, true);

    // Access by field index
    println!("Name: {}", person.0);
    println!("Age: {}", person.1);
    println!("Active: {}", person.2);

    // Destructuring: unpack tuple into variables
    let (name, age, active) = person;
    println!("Destructured -- {} is {} years old, active: {}", name, age, active);

    // Tuple as function return value
    let q1_sales = [120_000, 135_000, 128_000];  // Jan, Feb, Mar
    let (sum, count, avg) = analyze_sales(&q1_sales);
    println!("Q1 -- Sum: {}, Count: {}, Avg: {:.0}", sum, count, avg);

    // Nested tuples
    let nested = (1, (2.5, "hello"), true);
    println!("Nested: {:?}, inner: {}", nested, (nested.1).1);

    // Single-element tuple: note the trailing comma!
    let single = (42,);     // tuple with one element
    let not_tuple = (42);   // just a parenthesized integer
    println!("Single-element tuple: {:?}", single);
    println!("Not a tuple: {}", not_tuple);
}

Resultado:

TEXT 📖 Somente leitura
Name: Alice
Age: 30
Active: true
Destructured -- Alice is 30 years old, active: true
Q1 -- Sum: 383000, Count: 3, Avg: 127667
Nested: (1, (2.5, "hello"), true), inner: hello
Single-element tuple: (42,)
Not a tuple: 42

As tuplas podem armazenar valores de diferentes tipos. É possível acessar seus campos usando .0, .1 e .2, ou utilizar o padrão let (a, b, c) = tuple para desestruturação. As tuplas são particularmente adequadas para funções que retornam vários valores — sem a necessidade de definir uma estrutura. Observe que uma tupla com um único elemento deve ser delimitada por uma vírgula (42,).


▶ Exemplo 4: Iteração por matrizes e métodos comuns (Dificuldade ⭐⭐)

RUST
// ============================================
// Array iteration and common methods
// ============================================

fn main() {
    let revenue: [i32; 6] = [120_000, 135_000, 128_000, 142_000, 150_000, 165_000];
    let month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"];

    // Method 1: for-in (by value -- copies each element for i32)
    print!("All revenues: ");
    for r in revenue {
        print!("{} ", r);
    }
    println!();

    // Method 2: .iter() (by reference)
    print!("With .iter(): ");
    for r in revenue.iter() {
        print!("{} ", r);
    }
    println!();

    // Method 3: .iter().enumerate() (index + value)
    println!("\nMonthly report:");
    for (i, r) in revenue.iter().enumerate() {
        println!("  {}: {} yuan", month_names[i], r);
    }

    // Method 4: for i in 0..len (C-style index)
    println!("\nGrowth rate:");
    for i in 1..revenue.len() {
        let growth = (revenue[i] - revenue[i - 1]) as f64 / revenue[i - 1] as f64 * 100.0;
        println!("  {} -> {}: {:.1}%", month_names[i - 1], month_names[i], growth);
    }

    // Common array methods
    println!("\nSummary:");
    println!("  Total:     {}", revenue.iter().sum::<i32>());
    println!("  Max:       {}", revenue.iter().max().unwrap());
    println!("  Min:       {}", revenue.iter().min().unwrap());
    println!("  Count > 140k: {}", revenue.iter().filter(|&&r| r > 140_000).count());
}

Resultado:

TEXT 📖 Somente leitura
All revenues: 120000 135000 128000 142000 150000 165000
With .iter(): 120000 135000 128000 142000 150000 165000

Monthly report:
  Jan: 120000 yuan
  Feb: 135000 yuan
  Mar: 128000 yuan
  Apr: 142000 yuan
  May: 150000 yuan
  Jun: 165000 yuan

Growth rate:
  Jan -> Feb: 12.5%
  Feb -> Mar: -5.2%
  Mar -> Apr: 10.9%
  Apr -> May: 5.6%
  May -> Jun: 10.0%

Summary:
  Total:     840000
  Max:       165000
  Min:       120000
  Count > 140k: 3

Existem várias maneiras de iterar sobre um array: for val in arr (cópia de valores), arr.iter() (por referência), .enumerate() (indexado) e loops indexados no estilo C. Os arrays também oferecem um amplo conjunto de métodos iteradores, como .sum(), .max(), .min() e .filter().


▶ Exemplo 5: Exercício abrangente — Estatísticas de notas dos alunos (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example: Array + Tuple + Iterators in Practice
// ============================================

fn analyze(scores: &[i32]) -> (i32, i32, f64, i32) {
    let min = *scores.iter().min().unwrap_or(&0);
    let max = *scores.iter().max().unwrap_or(&0);
    let sum: i32 = scores.iter().sum();
    let avg = if scores.is_empty() { 0.0 } else { sum as f64 / scores.len() as f64 };
    let above_avg = scores.iter().filter(|&&s| s as f64 >= avg).count() as i32;
    (min, max, avg, above_avg)
}

fn classify(score: i32) -> &'static str {
    match score {
        90..=100 => "A",
        80..=89  => "B",
        70..=79  => "C",
        60..=69  => "D",
        _        => "F",
    }
}

fn main() {
    let students = [
        ("Alice",   [95, 88, 92]),
        ("Bob",     [72, 65, 58]),
        ("Charlie", [85, 90, 78]),
        ("David",   [60, 55, 70]),
    ];

    println!("=== Student Report Card ===");
    println!("{:<10} {:>6} {:>6} {:>6} {:>8} {:>6}", "Name", "Sub1", "Sub2", "Sub3", "Average", "Level");
    println!("{}", "-".repeat(44));

    let mut all_scores: Vec<i32> = Vec::new();

    for (name, scores) in &students {
        let (min, max, avg, above) = analyze(scores);
        let grade = classify(avg as i32);
        println!("{:<10} {:>6} {:>6} {:>6} {:>8.1} {:>6}",
            name, scores[0], scores[1], scores[2], avg, grade);
        all_scores.extend(scores.iter());
    }

    let (overall_min, overall_max, overall_avg, above_count) = analyze(&all_scores);
    println!("{}", "-".repeat(44));
    println!("Class-wide Statistics: Lowest={}, Highest={}, Average={:.1}, >=Average: {} people",
        overall_min, overall_max, overall_avg, above_count);

    let subject_avgs: [f64; 3] = [
        students.iter().map(|(_, s)| s[0] as f64).sum::<f64>() / students.len() as f64,
        students.iter().map(|(_, s)| s[1] as f64).sum::<f64>() / students.len() as f64,
        students.iter().map(|(_, s)| s[2] as f64).sum::<f64>() / students.len() as f64,
    ];
    println!("\nSubject Average: Sub1={:.1}, Sub2={:.1}, Sub3={:.1}",
        subject_avgs[0], subject_avgs[1], subject_avgs[2]);
}

Resultado:

TEXT 📖 Somente leitura
=== Student Report Card ===
Name         Sub1     Sub2     Sub3     Average    Level
--------------------------------------------
Alice         95      88      92     91.7      A
Bob           72      65      58     65.0      D
Charlie       85      90      78     84.3      B
David         60      55      70     61.7      D
--------------------------------------------
Class-wide Statistics: Lowest=55, Highest=95, Average=76.7, >=Average: 6 people

Subject Average: Sub1=78.0, Sub2=74.5, Sub3=74.5

Este exemplo combina o uso de matrizes e tuplas: [i32; 3] armazena as notas de cada aluno em três disciplinas; a tupla (&str, [i32; 3]) associa nomes e notas; analyze retorna a tupla (min, max, avg, count); e [f64; 3] armazena a nota média de cada disciplina.


❓ Perguntas Frequentes

P: Qual é a diferença entre um array [u8; 3] e uma fatia &[u8]? R: O tamanho de um array é fixado em tempo de compilação e alocado na pilha; uma fatia é uma visão de um array (ponteiro + comprimento) que pode apontar para uma parte do array. Um array é [T; N] (onde N é uma constante em tempo de compilação), enquanto uma fatia é &[T] (onde o comprimento é determinado em tempo de execução). As fatias são geralmente mais flexíveis quando usadas como parâmetros de função.

P: Qual é o número máximo de elementos que uma tupla pode ter? R: Teoricamente, até 12 elementos (a biblioteca padrão implementa características para tuplas com até 12 elementos). No entanto, se você tiver mais de 4 ou 5 campos, recomenda-se usar uma struct.

P: Por que o compilador não reporta um erro quando meu array ultrapassa os limites, mas entra em pânico em tempo de execução? R: O compilador só pode verificar acessos fora dos limites que possam ser determinados em tempo de compilação (como arr[100], em que o comprimento do array também é uma constante). A indexação dinâmica (como arr[i], em que i é fornecido pela entrada do usuário) só pode ser verificada em tempo de execução. Usar .get() é uma maneira segura de lidar com isso.

P: Tanto tuplas quanto estruturas são usadas para “agrupar vários valores” — quando se deve usar cada uma delas? R: As tuplas são adequadas para situações temporárias em que os campos não têm nomes semânticos (como, por exemplo, vários valores de retorno); as estruturas são adequadas para dados em que os campos têm significados claros e precisam ser reutilizados. Uma boa regra prática: se houver mais de três campos, ou se o mesmo tipo de tupla aparecer várias vezes no seu código, mude para uma estrutura.

P: [0; 5] Essa sintaxe funciona para todos os tipos? R: O tipo do elemento deve implementar a característica Copy. Por exemplo, [String; 3] não pode ser escrito dessa forma (String não é um tipo Copy). Nesse caso, é preciso inicializar cada elemento individualmente: ["a".into(), "b".into(), "c".into()].


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Declare uma matriz [f64; 7] de comprimento 7 para armazenar as temperaturas mais altas (em graus Celsius) de cada um dos 7 dias da semana. Use um loop para encontrar as temperaturas mais altas e mais baixas.
  2. Dificuldade ⭐⭐: Escreva uma função fn stats(arr: &[i32]) -> (i32, i32, f64) que retorne (o mínimo, o máximo e a média). Teste-a na função principal usando [10, 3, 7, 1, 9, 4].
  3. Dificuldade ⭐⭐⭐: Defina um array para armazenar as notas dos alunos [85, 92, 78, 90, 88]. Implemente manualmente uma função fn rank_scores(scores: &[i32]) -> Vec<(usize, i32, &str)> que retorne uma lista de tuplas ordenadas (classificação, nota, grau) (Regras de avaliação: >=90 é “A”, >=80 é “B”, >=70 é “C” e todas as outras são “D”).
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%