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
- Sintaxe para definir e inicializar matrizes
[T; N] - Acesso a elementos de um array por índice e verificações de ultrapassagem de limites
- Criação de tuplas e decomposição de padrões
- Uso de tuplas para implementar funções com vários valores de retorno
- Métodos para percorrer matrizes (loops “for”, “iter”, “enumerate”)
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:
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
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
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 ⭐)
// ============================================
// 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:
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. Usearr[i]para acessar elementos por índice efor val in arrpara 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 ⭐⭐)
// ============================================
// 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:
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 retornaOption<&T>, permitindo que você lide de forma adequada com condições fora dos limites usandomatchouunwrap_or, em vez de causar uma trava imediata.
▶ Exemplo 3: Criação e desempacotamento de tuplas e valores de retorno de funções (Dificuldade ⭐⭐)
// ============================================
// 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:
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,.1e.2, ou utilizar o padrãolet (a, b, c) = tuplepara 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 ⭐⭐)
// ============================================
// 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:
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 ⭐⭐⭐)
// ============================================
// 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:
=== 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;analyzeretorna 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 (comoarr[i], em queié 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ísticaCopy. 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
- Um array
[T; N]é uma coleção de elementos do mesmo tipo e comprimento fixo; seu tamanho é conhecido na hora da compilação e é alocado na pilha. - O acesso às matrizes é feito por meio do índice
arr[i]; um acesso fora dos limites causa um panic em tempo de execução; o método.get()retornaOptionpara um tratamento seguro - Sintaxe abreviada para matrizes
[val; N]Cria N elementos com o mesmo valor (Requer T: Cópia) - A tupla
(T1, T2)pode armazenar diferentes tipos, que podem ser acessados por meio de.0/.1ou por decomposição de padrões. - As tuplas são ideais para funções com vários valores de retorno—
fn foo() -> (i32, String) - Escolha entre tuplas e estruturas: use tuplas para dados temporários e estruturas para campos com significados específicos
📝 Exercícios
- 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. - 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]. - Dificuldade ⭐⭐⭐: Defina um array para armazenar as notas dos alunos
[85, 92, 78, 90, 88]. Implemente manualmente uma funçãofn 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”).