Rust Admin

Dominando Slices em Rust: Referências para Partes de Coleções em Projetos Reais Já leu

Entendendo Slices em Rust Um slice é uma referência a uma parte contígua de uma coleção, como um array ou string. Diferentemente de um vetor, que possui tamanho dinâmico e é dono dos dados, um slice é apenas uma "janela" para dados que já existem, sem transferir propriedade. Isso torna slices incrivelmente eficientes para passar partes de dados sem copiar ou alocar memória adicional. Em Rust, quando você trabalha com slices, está lidando com dois conceitos fundamentais: um ponteiro para o primeiro elemento e um comprimento. A linguagem garante através do seu sistema de empréstimo que essas referências são sempre válidas. Você encontrará slices em praticamente todo código Rust profissional, seja ao processar strings, arrays ou vetores. Sintaxe e Tipos de Slices Slices de Arrays e Vetores A sintaxe básica para criar um slice é usar a notação de intervalo . O índice inicial é inclusivo, enquanto o final é exclusivo: A anotação de tipo representa uma referência a um

Entendendo Slices em Rust

Um slice é uma referência a uma parte contígua de uma coleção, como um array ou string. Diferentemente de um vetor, que possui tamanho dinâmico e é dono dos dados, um slice é apenas uma "janela" para dados que já existem, sem transferir propriedade. Isso torna slices incrivelmente eficientes para passar partes de dados sem copiar ou alocar memória adicional.

Em Rust, quando você trabalha com slices, está lidando com dois conceitos fundamentais: um ponteiro para o primeiro elemento e um comprimento. A linguagem garante através do seu sistema de empréstimo que essas referências são sempre válidas. Você encontrará slices em praticamente todo código Rust profissional, seja ao processar strings, arrays ou vetores.

Sintaxe e Tipos de Slices

Slices de Arrays e Vetores

A sintaxe básica para criar um slice é usar a notação de intervalo [início..fim]. O índice inicial é inclusivo, enquanto o final é exclusivo:

fn main() {
    let numeros = vec![10, 20, 30, 40, 50];

    // Slice do índice 1 ao 3 (não inclui 3)
    let slice: &[i32] = &numeros[1..3];
    println!("{:?}", slice); // [20, 30]

    // Do início até índice 2
    let inicio = &numeros[..2];
    println!("{:?}", inicio); // [10, 20]

    // Do índice 2 até o final
    let fim = &numeros[2..];
    println!("{:?}", fim); // [30, 40, 50]

    // Slice inteiro
    let tudo = &numeros[..];
    println!("{:?}", tudo); // [10, 20, 30, 40, 50]
}

A anotação de tipo &[i32] representa uma referência a um slice de inteiros de 32 bits. Note que o tamanho não está especificado no tipo — isso é exatamente o ponto: slices têm tamanho dinâmico em tempo de compilação.

String Slices

String slices (&str) são particularmente importantes em Rust. Eles representam uma sequência válida de UTF-8 bytes. Uma string literal já é um slice:

fn main() {
    let mensagem = "Olá, Rust!";
    let tipo: &str = mensagem; // Automaticamente um slice

    // Extraindo parte da string
    let parte = &mensagem[0..3];
    println!("{}", parte); // "Olá"

    // Função que aceita um slice de string
    processar_texto(&mensagem);
    processar_texto("String literal");
}

fn processar_texto(texto: &str) {
    println!("Recebido: {}", texto);
}

Quando você passa uma String para uma função que espera &str, Rust converte automaticamente — isso é chamado coerção de tipo. Essa flexibilidade é um dos grandes poderes do design de slices.

O Sistema de Empréstimo e Segurança

Garantias de Segurança

Slices funcionam em harmonia perfeita com o sistema de propriedade e empréstimo de Rust. Enquanto uma referência a um slice está ativa, os dados subjacentes não podem ser modificados (se você possui uma referência imutável) nem movidos. Isso previne dangling pointers — um problema clássico de linguagens como C:

fn main() {
    let mut vetor = vec![1, 2, 3, 4, 5];
    let slice = &vetor[1..3]; // Empréstimo imutável

    // Isto causaria erro de compilação:
    // vetor.push(6); // Não pode modificar enquanto slice existe

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

    // Após sair do escopo de 'slice', posso modificar novamente
    vetor.push(6);
    println!("{:?}", vetor);
}

Se você precisar modificar os dados, use um slice mutável. Mas lembre-se: apenas uma referência mutável pode existir por vez:

fn main() {
    let mut vetor = vec![10, 20, 30];
    let slice_mut = &mut vetor[0..2];

    slice_mut[0] = 15; // Modificação via slice mutável
    println!("{:?}", vetor); // [15, 20, 30]
}

Padrões Práticos com Slices

Funções que Trabalham com Partes de Dados

Slices são perfeitos para escrever funções genéricas que funcionam com qualquer tamanho de dados. Aqui está um exemplo prático — uma função que encontra a soma dos números em um slice:

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

fn maior_elemento(slice: &[i32]) -> Option<i32> {
    if slice.is_empty() {
        None
    } else {
        Some(*slice.iter().max().unwrap())
    }
}

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let vetor = vec![10, 20, 30];

    // Funciona com arrays
    println!("Soma: {}", soma_slice(&arr));

    // Funciona com vetores (convertidos para slices)
    println!("Soma: {}", soma_slice(&vetor));

    // Funciona com partes específicas
    println!("Maior: {:?}", maior_elemento(&arr[1..4]));
}

Iteração Eficiente

Slices trabalham perfeitamente com iteradores, sem necessidade de copiar dados:

fn filtrar_pares(numeros: &[i32]) -> Vec<i32> {
    numeros
        .iter()
        .filter(|n| *n % 2 == 0)
        .copied()
        .collect()
}

fn main() {
    let dados = vec![1, 2, 3, 4, 5, 6];
    let pares = filtrar_pares(&dados);
    println!("{:?}", pares); // [2, 4, 6]
}

Conclusão

Slices são um pilar do Rust moderno. Eles oferecem três benefícios fundamentais que você deve internalizar: (1) segurança de memória garantida pelo compilador através do sistema de empréstimo; (2) eficiência permitindo trabalhar com partes de dados sem copiar ou alocar; e (3) flexibilidade permitindo escrever funções que funcionam com arrays, vetores e strings de forma uniforme. Domine slices e você entenderá o coração do design de Rust.

Referências


Artigos relacionados