Slice: referências para partes de coleções

1. O que é um Slice e por que ele existe?

Um slice (ou "fatia") é uma referência para uma sequência contígua de elementos dentro de uma coleção, sem possuir a propriedade (ownership) dos dados. Diferente de um vetor ou array que detém seus elementos, um slice apenas "empresta" uma visão parcial ou total de uma coleção existente.

A diferença fundamental é que o slice não aloca nem gerencia memória — ele apenas aponta para um bloco de dados que pertence a outra variável. Pense em um slice como uma janela: você pode ver e interagir com o conteúdo, mas não pode mover a janela para outro lugar sem permissão.

Essa abstração é poderosa porque permite que funções operem sobre partes de coleções sem precisar copiar dados, economizando memória e tempo de processamento.

2. Slice de Arrays e Vetores: &[T]

A sintaxe básica para criar slices usa intervalos (ranges): &colecao[inicio..fim]. O intervalo é exclusivo no final.

let v = vec![1, 2, 3, 4, 5];
let slice = &v[1..4]; // elementos 2, 3, 4

println!("Slice: {:?}", slice); // [2, 3, 4]

Existem variações úteis:
- &v[..3] — do início até o índice 3 (exclusivo)
- &v[2..] — do índice 2 até o final
- &v[..] — o vetor inteiro como slice

let v = vec![10, 20, 30, 40, 50];

let inicio = &v[..2];   // [10, 20]
let fim = &v[3..];      // [40, 50]
let completo = &v[..];  // [10, 20, 30, 40, 50]

println!("Início: {:?}", inicio);
println!("Fim: {:?}", fim);
println!("Completo: {:?}", completo);

O tipo resultante é &[i32] — uma referência imutável para uma fatia do vetor original. Como o slice toma emprestado os dados, as regras de borrowing se aplicam: enquanto o slice existir, você não pode modificar o vetor original de forma que invalide a referência.

3. Slice de Strings: &str como um caso especial

Em Rust, &str é essencialmente um slice de uma String (ou de uma string literal). A relação entre String e &str é análoga à relação entre Vec<T> e &[T].

let s = String::from("hello");
let slice = &s[0..2]; // "he"

println!("Slice: {}", slice);

Cuidado com índices de bytes: strings em Rust são codificadas em UTF-8, e cada caractere pode ocupar mais de um byte. Índices incorretos podem causar panic em tempo de execução.

let texto = String::from("Olá, mundo!");
// Erro! O caractere 'á' ocupa 2 bytes, então índice 3 está no meio dele
// let slice_invalido = &texto[0..3]; // PANIC!

// Correto: usar índices que respeitam limites de caracteres
let slice_valido = &texto[0..4]; // "Olá"
println!("Slice válido: {}", slice_valido);

&str vs String: Use &str para parâmetros de funções que só precisam ler strings (mais flexível e eficiente). Use String quando precisar modificar ou possuir a string.

4. Slice Mútavel: &mut [T]

Slices mutáveis permitem modificar os elementos da coleção original através da referência.

let mut vetor = vec![1, 2, 3, 4, 5];
let slice_mut = &mut vetor[1..4]; // referência mutável para [2, 3, 4]

slice_mut[0] = 99; // modifica o elemento no índice 1 do vetor original
slice_mut[2] = 77; // modifica o elemento no índice 3 do vetor original

println!("Vetor modificado: {:?}", vetor); // [1, 99, 3, 77, 5]

Restrições de borrowing: Não é possível ter um slice mutável e outro imutável ao mesmo tempo.

let mut v = vec![1, 2, 3];
let s1 = &v[..];      // borrow imutável
// let s2 = &mut v[..]; // ERRO! já existe um borrow imutável

Slices mutáveis não funcionam com strings devido à codificação UTF-8 — modificar bytes individuais poderia quebrar a validade da string.

5. Operações e Métodos Comuns em Slices

Slices oferecem diversos métodos úteis para manipulação e consulta:

let slice = &[10, 20, 30, 40, 50];

// Acessando elementos com segurança
println!("Primeiro: {:?}", slice.first());  // Some(10)
println!("Último: {:?}", slice.last());     // Some(50)
println!("Índice 2: {:?}", slice.get(2));   // Some(30)
println!("Índice 10: {:?}", slice.get(10)); // None

// Propriedades
println!("Tamanho: {}", slice.len());      // 5
println!("Está vazio?: {}", slice.is_empty()); // false

// Iteração
for elem in slice {
    print!("{} ", elem); // 10 20 30 40 50
}

// Sub-fatiamento
let sub_slice = &slice[1..3]; // [20, 30]
println!("\nSub-slice: {:?}", sub_slice);

// Busca
println!("Contém 30?: {}", slice.contains(&30)); // true

// Ordenação (requer slice mutável)
let mut dados = vec![5, 3, 1, 4, 2];
dados.sort();
println!("Ordenado: {:?}", dados); // [1, 2, 3, 4, 5]

6. Slices como Parâmetros de Funções

Usar &[T] como parâmetro é mais flexível que &Vec<T>, pois aceita arrays, vetores e outros slices.

fn sum(slice: &[i32]) -> i32 {
    slice.iter().sum()
}

// Funciona com vetor
let vec = vec![1, 2, 3];
println!("Soma do vetor: {}", sum(&vec));

// Funciona com array
let arr = [4, 5, 6];
println!("Soma do array: {}", sum(&arr));

// Funciona com slice literal
println!("Soma do literal: {}", sum(&[7, 8, 9]));

// Funciona com sub-slice
println!("Soma do sub-slice: {}", sum(&vec[0..2]));

Isso funciona devido à coerção de tipos via trait Deref: &Vec<T> e &[T; N] são convertidos automaticamente para &[T]. A boa prática é preferir &[T] em parâmetros de funções que só precisam ler dados sequenciais.

7. Casos Especiais e Armadilhas Comuns

Slice vazio: Útil como valor padrão ou para representar "nenhum elemento".

let vazio: &[i32] = &[];
println!("Slice vazio: {:?}", vazio); // []

Slice de um array fixo: Arrays podem ser convertidos em slices completos.

let arr = [1, 2, 3];
let s: &[i32] = &arr[..];
println!("Array como slice: {:?}", s);

Erro comum: Tentar criar slice mutável de string não compila.

let mut s = String::from("teste");
// let slice_mut = &mut s[0..2]; // ERRO! não é possível

Panic com índices fora dos limites: Sempre prefira slice.get() para acesso seguro.

let slice = &[1, 2, 3];
// let val = slice[10]; // PANIC! índice fora dos limites

match slice.get(10) {
    Some(val) => println!("Valor: {}", val),
    None => println!("Índice inválido"),
}

Slices são uma das abstrações mais elegantes de Rust, combinando segurança de memória com eficiência. Dominá-los é essencial para escrever código idiomático e performático.


Referências