O que são Slices
Um slice (&[T]) é uma referência a uma sequência contígua de elementos do tipo T. Ele não possui os dados — apenas aponta para eles. Internamente, um slice é composto por dois valores: um ponteiro para o primeiro elemento e um comprimento. Essa representação simples permite que slices referenciem partes de arrays, vetores, ou qualquer bloco de memória contíguo.
Slices são fundamentais em Rust porque permitem trabalhar com sequências de dados de forma genérica, sem se preocupar se a origem é um array [T; N], um Vec<T>, ou outra estrutura. Ao aceitar &[T] como parâmetro de função (em vez de &Vec<T>), seu código se torna mais flexível e idiomático. A versão mutável &mut [T] permite modificar os elementos sem realocar.
Criando Slices
fn main() {
// A partir de um array
let array = [10, 20, 30, 40, 50];
let slice_completo: &[i32] = &array; // Todo o array
let parcial: &[i32] = &array[1..4]; // [20, 30, 40]
let do_inicio: &[i32] = &array[..3]; // [10, 20, 30]
let ate_final: &[i32] = &array[2..]; // [30, 40, 50]
// A partir de um Vec
let vec = vec![1, 2, 3, 4, 5];
let slice_vec: &[i32] = &vec; // Coerção automática
let parte: &[i32] = &vec[1..3]; // [2, 3]
// Slice mutável
let mut dados = vec![5, 3, 1, 4, 2];
let slice_mut: &mut [i32] = &mut dados;
slice_mut.sort();
println!("Ordenado: {slice_mut:?}"); // [1, 2, 3, 4, 5]
// Slice vazio
let vazio: &[i32] = &[];
// A partir de uma String (slice de bytes)
let texto = String::from("Rust");
let bytes: &[u8] = texto.as_bytes();
println!("{slice_completo:?} | {parcial:?} | {do_inicio:?} | {ate_final:?}");
println!("{slice_vec:?} | {parte:?} | {vazio:?} | {bytes:?}");
}
Tabela de Métodos Principais
| Método | Descrição | Exemplo |
|---|---|---|
len() | Número de elementos | s.len() → 5 |
is_empty() | Verifica se está vazio | s.is_empty() |
first() | Primeiro elemento (Option<&T>) | s.first() → Some(&1) |
last() | Último elemento (Option<&T>) | s.last() → Some(&5) |
get(i) | Acesso seguro por índice | s.get(2) → Some(&3) |
contains(&val) | Verifica se contém o valor | s.contains(&3) → true |
starts_with(sub) | Verifica se começa com o subslice | s.starts_with(&[1, 2]) |
ends_with(sub) | Verifica se termina com o subslice | s.ends_with(&[4, 5]) |
iter() | Iterador de &T | s.iter() |
iter_mut() | Iterador de &mut T | s.iter_mut() |
windows(n) | Janelas deslizantes de tamanho n | s.windows(3) |
chunks(n) | Pedaços de tamanho n | s.chunks(2) |
chunks_exact(n) | Pedaços exatos (descarta resto) | s.chunks_exact(3) |
split(f) | Divide onde f é true | s.split(|x| *x == 0) |
splitn(n, f) | Divide em no máximo n partes | s.splitn(2, |x| *x == 0) |
split_at(i) | Divide em dois slices na posição i | s.split_at(3) |
sort() | Ordena in-place (requer &mut [T]) | s.sort() |
sort_by(f) | Ordena com comparador | s.sort_by(|a, b| b.cmp(a)) |
sort_unstable() | Ordena (pode ser mais rápido) | s.sort_unstable() |
binary_search(&val) | Busca binária (requer slice ordenado) | s.binary_search(&5) |
reverse() | Inverte in-place | s.reverse() |
swap(i, j) | Troca elementos nas posições i e j | s.swap(0, 2) |
rotate_left(n) | Rotaciona para a esquerda | s.rotate_left(2) |
fill(val) | Preenche com um valor | s.fill(0) |
copy_from_slice(src) | Copia de outro slice (mesmo tamanho) | dest.copy_from_slice(src) |
to_vec() | Cria um Vec<T> a partir do slice | s.to_vec() |
concat() | Concatena slices aninhados | [&[1,2], &[3,4]].concat() |
join(sep) | Junta com separador | Disponível para &[&str] etc. |
Exemplos Práticos
1. Processamento com windows e chunks
fn media_movel(dados: &[f64], janela: usize) -> Vec<f64> {
dados
.windows(janela)
.map(|w| w.iter().sum::<f64>() / w.len() as f64)
.collect()
}
fn processar_em_lotes(dados: &[i32], tamanho_lote: usize) {
for (i, lote) in dados.chunks(tamanho_lote).enumerate() {
let soma: i32 = lote.iter().sum();
let media = soma as f64 / lote.len() as f64;
println!("Lote {}: {:?} (média: {media:.1})", i + 1, lote);
}
}
fn main() {
let precos = vec![100.0, 105.0, 98.0, 110.0, 107.0, 115.0, 112.0];
let medias = media_movel(&precos, 3);
println!("Médias móveis (3): {medias:.1?}");
println!();
let dados: Vec<i32> = (1..=15).collect();
processar_em_lotes(&dados, 4);
}
2. Busca Binária em Dados Ordenados
fn main() {
let dados = vec![2, 5, 8, 12, 16, 23, 38, 56, 72, 91];
// binary_search retorna Result<usize, usize>
// Ok(indice) se encontrado, Err(indice_insercao) se não
match dados.binary_search(&23) {
Ok(pos) => println!("23 encontrado na posição {pos}"), // posição 5
Err(pos) => println!("23 não encontrado, inserir em {pos}"),
}
match dados.binary_search(&15) {
Ok(pos) => println!("15 encontrado na posição {pos}"),
Err(pos) => println!("15 não encontrado, inserir em {pos}"), // posição 4
}
// binary_search_by para tipos customizados
let nomes = vec!["Ana", "Bruno", "Carlos", "Diana", "Eduardo"];
match nomes.binary_search_by(|probe| probe.cmp(&"Carlos")) {
Ok(pos) => println!("Carlos na posição {pos}"),
Err(_) => println!("Não encontrado"),
}
// Inserir mantendo a ordem
let mut ordenado = vec![1, 3, 5, 7, 9];
let novo = 6;
let pos = ordenado.binary_search(&novo).unwrap_or_else(|x| x);
ordenado.insert(pos, novo);
println!("Após inserir {novo}: {ordenado:?}"); // [1, 3, 5, 6, 7, 9]
}
3. split e Padrões de Divisão
fn main() {
let dados = [1, 0, 2, 3, 0, 4, 5, 6, 0, 7];
// split — divide onde o predicado é verdadeiro
println!("Split por zeros:");
for grupo in dados.split(|x| *x == 0) {
println!(" {grupo:?}");
}
// [1], [2, 3], [4, 5, 6], [7]
// splitn — limita o número de divisões
println!("\nSplit (máx 2 partes):");
for grupo in dados.splitn(2, |x| *x == 0) {
println!(" {grupo:?}");
}
// [1], [2, 3, 0, 4, 5, 6, 0, 7]
// split_at — divide em posição específica
let numeros = [10, 20, 30, 40, 50];
let (esquerda, direita) = numeros.split_at(3);
println!("\nEsquerda: {esquerda:?}"); // [10, 20, 30]
println!("Direita: {direita:?}"); // [40, 50]
// rsplit — divide da direita para a esquerda
println!("\nRsplit por zeros:");
for grupo in dados.rsplit(|x| *x == 0) {
println!(" {grupo:?}");
}
// [7], [4, 5, 6], [2, 3], [1]
}
4. Funções Genéricas que Aceitam Slices
fn encontrar_maximo<T: PartialOrd>(slice: &[T]) -> Option<&T> {
slice.iter().reduce(|max, x| if x > max { x } else { max })
}
fn estatisticas(dados: &[f64]) -> Option<(f64, f64, f64)> {
if dados.is_empty() {
return None;
}
let soma: f64 = dados.iter().sum();
let media = soma / dados.len() as f64;
let variancia = dados.iter()
.map(|x| (x - media).powi(2))
.sum::<f64>() / dados.len() as f64;
let desvio_padrao = variancia.sqrt();
Some((media, variancia, desvio_padrao))
}
fn imprimir_tabela(cabecalho: &[&str], dados: &[&[&str]]) {
// Calcular largura de cada coluna
let larguras: Vec<usize> = (0..cabecalho.len())
.map(|col| {
let max_dados = dados.iter()
.filter_map(|linha| linha.get(col))
.map(|s| s.len())
.max()
.unwrap_or(0);
cabecalho[col].len().max(max_dados)
})
.collect();
// Imprimir cabeçalho
for (i, h) in cabecalho.iter().enumerate() {
print!("| {:<width$} ", h, width = larguras[i]);
}
println!("|");
// Separador
for w in &larguras {
print!("|{}", "-".repeat(w + 2));
}
println!("|");
// Dados
for linha in dados {
for (i, celula) in linha.iter().enumerate() {
print!("| {:<width$} ", celula, width = larguras[i]);
}
println!("|");
}
}
fn main() {
// Aceita Vec, array ou slice — todos coercem para &[T]
let vec = vec![3.2, 1.8, 4.5, 2.1, 3.7];
let array = [10.0, 20.0, 30.0];
println!("Max vec: {:?}", encontrar_maximo(&vec)); // Some(4.5)
println!("Max array: {:?}", encontrar_maximo(&array)); // Some(30.0)
if let Some((media, var, dp)) = estatisticas(&vec) {
println!("Média: {media:.2}, Variância: {var:.2}, DP: {dp:.2}");
}
println!();
let cabecalho = ["Nome", "Linguagem", "Nível"];
let dados: Vec<&[&str]> = vec![
&["Ana", "Rust", "Avançado"],
&["Bruno", "Python", "Intermediário"],
&["Carlos", "Go", "Iniciante"],
];
imprimir_tabela(&cabecalho, &dados);
}
5. Operações In-Place com Slices Mutáveis
fn particionar<T: PartialOrd + Copy>(slice: &mut [T], pivot: T) -> usize {
let mut i = 0;
for j in 0..slice.len() {
if slice[j] <= pivot {
slice.swap(i, j);
i += 1;
}
}
i
}
fn main() {
// sort_unstable — geralmente mais rápido que sort
let mut numeros = vec![38, 27, 43, 3, 9, 82, 10];
numeros.sort_unstable();
println!("Ordenado: {numeros:?}");
// sort_by_key — ordenar por critério
let mut palavras = vec!["banana", "uva", "abacaxi", "kiwi", "maçã"];
palavras.sort_by_key(|p| p.len());
println!("Por tamanho: {palavras:?}");
// reverse
let mut seq = vec![1, 2, 3, 4, 5];
seq.reverse();
println!("Invertido: {seq:?}"); // [5, 4, 3, 2, 1]
// rotate_left / rotate_right
let mut fila = vec!["A", "B", "C", "D", "E"];
fila.rotate_left(2);
println!("Rotação esquerda: {fila:?}"); // ["C", "D", "E", "A", "B"]
// fill — preencher com um valor
let mut buffer = vec![0u8; 10];
buffer[3..7].fill(0xFF);
println!("Buffer: {buffer:?}");
// swap
let mut dados = vec![10, 20, 30, 40, 50];
dados.swap(1, 3);
println!("Após swap: {dados:?}"); // [10, 40, 30, 20, 50]
// Particionar
let mut vals = vec![7, 2, 9, 1, 5, 8, 3, 6, 4];
let pos = particionar(&mut vals, 5);
println!("Particionado (pivot=5): {vals:?}, posição: {pos}");
}
Dicas de Performance e Armadilhas
Prefira
&[T]a&Vec<T>em parâmetros: Aceitar&[T]torna sua função mais genérica — ela funciona com arrays, vetores e outros slices.Vec<T>implementaDeref<Target=[T]>, então a conversão é automática.Indexação pode causar panic:
slice[i]gera panic sei >= slice.len(). Useslice.get(i)para acesso seguro comOption<&T>.sort_unstablevssort:sort_unstable()é geralmente mais rápido e usa menos memória quesort(), mas não preserva a ordem de elementos iguais. Usesort()quando a estabilidade importa.chunks_exactdescarta o resto: Se o tamanho do slice não é múltiplo do tamanho do chunk,chunks_exactignora os elementos restantes. Usechunks_exact(n).remainder()para acessar o que sobrou.Copy semântico com slices: Slices são referências e, portanto,
Copy. Passar&[T]é barato independente do tamanho — apenas um ponteiro e um comprimento são copiados.split_atpara processamento paralelo: Dividir um slice mutável em duas metades comsplit_at_mutpermite processá-las em paralelo sem violar as regras de borrowing.
Veja Também
- Filtrar Vetores em Rust — receitas práticas de filtragem
- Vec<T>: Vetor Dinâmico — o tipo que mais produz slices
- String e &str —
&stré essencialmente um&[u8]com garantia UTF-8 - Iterator Trait — slices fornecem iteradores nativos
- Cow<T>: Clone on Write —
Cow<[T]>para slices com ownership flexível - Cheatsheet Rust — referência rápida de sintaxe
- Documentação oficial — primitive slice