Os ranges (faixas) em Rust são tipos que representam intervalos de valores. Eles são usados extensivamente em for loops, fatiamento de slices, verificação de pertinência e consultas em coleções ordenadas. A sintaxe com .. e ..= é compacta e expressiva, sendo um dos recursos mais utilizados da linguagem.
Tipos de Range
Rust possui seis tipos de range, todos no módulo std::ops:
| Sintaxe | Tipo | Intervalo Matemático | Exemplo |
|---|---|---|---|
a..b | Range<T> | [a, b) | 0..10 = 0 a 9 |
a..=b | RangeInclusive<T> | [a, b] | 0..=10 = 0 a 10 |
a.. | RangeFrom<T> | [a, +inf) | 5.. = 5, 6, 7, … |
..b | RangeTo<T> | (-inf, b) | ..5 = até 4 |
..=b | RangeToInclusive<T> | (-inf, b] | ..=5 = até 5 |
.. | RangeFull | (-inf, +inf) | .. = tudo |
Criando e Usando Ranges
use std::ops::{Range, RangeInclusive, RangeFrom, RangeTo, RangeFull};
fn main() {
// Range exclusivo: [0, 5)
let r1: Range<i32> = 0..5;
println!("0..5 = {:?}", r1);
// Range inclusivo: [0, 5]
let r2: RangeInclusive<i32> = 0..=5;
println!("0..=5 = {:?}", r2);
// Range aberto à direita: [3, +inf)
let r3: RangeFrom<i32> = 3..;
println!("3.. = {:?}", r3);
// Range aberto à esquerda: (-inf, 5)
let r4: RangeTo<i32> = ..5;
println!("..5 = {:?}", r4);
// Range full (todos os elementos)
let r5: RangeFull = ..;
// Ranges com outros tipos
let letras = 'a'..='z';
let floats = 0.0..1.0;
println!("Letras: {:?}", letras);
println!("Floats: {:?}", floats);
}
Tabela de Operações
| Operação | Funciona Com | Descrição |
|---|---|---|
for x in range | Range, RangeInclusive, RangeFrom | Iteração |
slice[range] | Todos os tipos | Fatiamento |
range.contains(&v) | Todos os tipos | Verificação de pertinência |
range.is_empty() | Range, RangeInclusive | Verifica se o intervalo é vazio |
range.count() | Range, RangeInclusive | Número de elementos (consome) |
range.sum() | Range, RangeInclusive | Soma dos elementos (consome) |
range.collect() | Range, RangeInclusive | Coleta em coleção |
range.rev() | Range, RangeInclusive | Iteração reversa |
range.step_by(n) | Range, RangeInclusive | Passo customizado |
range.zip(other) | Range, RangeInclusive | Combina com outro iterador |
range.enumerate() | Range, RangeInclusive | Adiciona índice |
range.map(f) | Range, RangeInclusive | Transforma elementos |
range.filter(f) | Range, RangeInclusive | Filtra elementos |
Exemplos Práticos
1. Ranges em For Loops
fn main() {
// Range exclusivo: 0, 1, 2, 3, 4
print!("0..5: ");
for i in 0..5 {
print!("{} ", i);
}
println!();
// Range inclusivo: 1, 2, 3, 4, 5
print!("1..=5: ");
for i in 1..=5 {
print!("{} ", i);
}
println!();
// Range reverso
print!("Rev: ");
for i in (1..=5).rev() {
print!("{} ", i);
}
println!();
// Range com passo (step_by)
print!("Step 2: ");
for i in (0..10).step_by(2) {
print!("{} ", i);
}
println!();
// Range de caracteres
print!("a..=f: ");
for c in 'a'..='f' {
print!("{} ", c);
}
println!();
// Contagem regressiva (countdown)
print!("10..1: ");
for i in (1..=10).rev() {
print!("{} ", i);
}
println!("Lançar!");
// Usando range com variáveis
let inicio = 5;
let fim = 10;
let soma: i32 = (inicio..=fim).sum();
println!("\nSoma de {}..={}: {}", inicio, fim, soma); // 45
}
2. Ranges para Fatiamento de Slices e Strings
fn main() {
let dados = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
// Fatias com diferentes tipos de range
let primeiros = &dados[..3]; // [10, 20, 30]
let meios = &dados[3..7]; // [40, 50, 60, 70]
let ultimos = &dados[7..]; // [80, 90, 100]
let ate_5 = &dados[..=4]; // [10, 20, 30, 40, 50]
let tudo = &dados[..]; // todos os elementos
println!("Primeiros 3: {:?}", primeiros);
println!("Do 4o ao 7o: {:?}", meios);
println!("Últimos 3: {:?}", ultimos);
println!("Até 5o (incl.): {:?}", ate_5);
println!("Tudo: {:?}", tudo);
// Fatiamento de strings
let texto = "Olá, Mundo!";
let ola = &texto[..5]; // cuidado: índices em bytes, não caracteres!
let mundo = &texto[6..11];
println!("{} -> {}", ola, mundo);
// Fatiamento de Vec
let mut vetor = vec![1, 2, 3, 4, 5];
let fatia = &vetor[1..4];
println!("Fatia do Vec: {:?}", fatia); // [2, 3, 4]
// Modificar fatia
for elem in &mut vetor[2..] {
*elem *= 10;
}
println!("Vetor modificado: {:?}", vetor); // [1, 2, 30, 40, 50]
}
3. contains — Verificação de Pertinência
fn main() {
// contains funciona com todos os tipos de range
let faixa = 10..50;
println!("25 em 10..50? {}", faixa.contains(&25)); // true
println!("50 em 10..50? {}", faixa.contains(&50)); // false (exclusivo!)
println!("50 em 10..=50? {}", (10..=50).contains(&50)); // true (inclusivo!)
// Ranges abertos
println!("5 em ..10? {}", (..10).contains(&5)); // true
println!("15 em 10..? {}", (10..).contains(&15)); // true
// Com caracteres
let minusculas = 'a'..='z';
let maiusculas = 'A'..='Z';
let digitos = '0'..='9';
let c = 'R';
if maiusculas.contains(&c) {
println!("'{}' é maiúscula", c);
}
// Classificar caracteres
for ch in "Rust 2026!".chars() {
let tipo = if ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) {
"letra"
} else if digitos.contains(&ch) {
"dígito"
} else {
"outro"
};
println!(" '{}' -> {}", ch, tipo);
}
}
4. Ranges com Iteradores
fn main() {
// collect para criar coleções
let numeros: Vec<i32> = (1..=10).collect();
println!("1..=10: {:?}", numeros);
// map + collect
let quadrados: Vec<i32> = (1..=5).map(|x| x * x).collect();
println!("Quadrados: {:?}", quadrados); // [1, 4, 9, 16, 25]
// filter + collect
let pares: Vec<i32> = (1..=20).filter(|x| x % 2 == 0).collect();
println!("Pares: {:?}", pares);
// Operações de agregação
let soma: i64 = (1..=100).sum();
let produto: i64 = (1..=10).product();
let contagem = (1..=1000).filter(|x| x % 7 == 0).count();
println!("Soma 1..=100: {}", soma); // 5050
println!("Produto 1..=10: {}", produto); // 3628800 (10!)
println!("Múltiplos de 7 até 1000: {}", contagem);
// zip com dois ranges
let pares: Vec<(i32, i32)> = (0..5).zip(10..15).collect();
println!("Zip: {:?}", pares);
// [(0, 10), (1, 11), (2, 12), (3, 13), (4, 14)]
// enumerate (equivalente a zip com 0..)
for (i, letra) in ('a'..='e').enumerate() {
println!(" {}: {}", i, letra);
}
// Criar string com range
let alfabeto: String = ('A'..='Z').collect();
println!("Alfabeto: {}", alfabeto);
// flat_map com ranges
let tabuada: Vec<String> = (1..=3)
.flat_map(|a| (1..=3).map(move |b| format!("{}x{}={}", a, b, a * b)))
.collect();
println!("Tabuada: {:?}", tabuada);
}
5. Ranges em Coleções Ordenadas (BTreeMap/BTreeSet)
use std::collections::BTreeMap;
use std::collections::BTreeSet;
fn main() {
// BTreeMap com range queries
let mut temperaturas: BTreeMap<u32, f64> = BTreeMap::new();
for hora in 0..24 {
let temp = 15.0 + 10.0 * ((hora as f64 - 14.0) / 12.0 * std::f64::consts::PI).sin();
temperaturas.insert(hora, (temp * 10.0).round() / 10.0);
}
// Temperatura entre 8h e 18h
println!("Temperaturas do dia (8h-18h):");
for (hora, temp) in temperaturas.range(8..=18) {
println!(" {:2}h: {:.1}°C", hora, temp);
}
// BTreeSet com range
let numeros: BTreeSet<i32> = (1..=100).collect();
// Números entre 40 e 50
let faixa: Vec<&i32> = numeros.range(40..=50).collect();
println!("\nNúmeros 40-50: {:?}", faixa);
// Contar elementos em uma faixa
let qtd = numeros.range(1..=25).count();
println!("Quantidade 1-25: {}", qtd);
// Range em match
let nota = 7;
let conceito = match nota {
0..=3 => "Insuficiente",
4..=5 => "Regular",
6..=7 => "Bom",
8..=9 => "Muito Bom",
10 => "Excelente",
_ => "Inválido",
};
println!("\nNota {}: {}", nota, conceito);
}
Características de Desempenho
Ranges são zero-cost abstractions em Rust. O compilador otimiza loops com ranges para o mesmo código de máquina que um loop C com contador.
// Estas duas versões geram código de máquina idêntico:
// Versão Rust idiomática
fn soma_range(n: i64) -> i64 {
(1..=n).sum()
}
// Versão "manual"
fn soma_manual(n: i64) -> i64 {
let mut total = 0;
let mut i = 1;
while i <= n {
total += i;
i += 1;
}
total
}
fn main() {
println!("{}", soma_range(100)); // 5050
println!("{}", soma_manual(100)); // 5050
}
| Aspecto | Custo |
|---|---|
| Criação de range | O(1), sem alocação |
Iteração for x in a..b | O(b-a), zero overhead |
contains | O(1) — simples comparação |
count | O(1) para Range, O(n) para filtrado |
sum | O(n) para iteração, O(1) se otimizado |
Fatiamento &arr[a..b] | O(1) — retorna ponteiro + tamanho |
Memória: Um Range<i32> ocupa apenas 8 bytes (dois i32). Um RangeInclusive<i32> ocupa 12 bytes (dois i32 + flag de exaustão). Ranges não alocam memória no heap.
Ranges vs Outras Formas de Iteração
| Abordagem | Quando Usar |
|---|---|
for i in 0..n | Iteração por índice |
for x in &coleção | Iteração por referência |
for x in coleção | Iteração consumindo (move) |
(0..n).map(f) | Transformação funcional |
(0..n).filter(f) | Filtragem |
loop { ... } | Loop infinito com controle manual |
Veja Também
- Slices em Rust — fatias de dados que usam ranges para indexação
- Iterator em Rust — trait Iterator usado por ranges
- Arrays em Rust — arrays de tamanho fixo fatiáveis com ranges
- BTreeMap: Mapa Ordenado — range queries com mapas
- BTreeSet: Conjunto Ordenado — range queries com conjuntos
- Variáveis, Tipos e Funções — fundamentos de tipos e loops