Rust

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

7 min de leitura

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

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

<h2>Entendendo Slices em Rust</h2>

<p>Um slice é uma <strong>referência a uma parte contígua de uma coleção</strong>, como um array ou string. Diferentemente de um vetor, que possui tamanho dinâmico e é dono dos dados, um slice é apenas uma &quot;janela&quot; 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.</p>

<p>Em Rust, quando você trabalha com slices, está lidando com dois conceitos fundamentais: um <strong>ponteiro para o primeiro elemento</strong> e um <strong>comprimento</strong>. 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.</p>

<h2>Sintaxe e Tipos de Slices</h2>

<h3>Slices de Arrays e Vetores</h3>

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

<pre><code class="language-rust">fn main() {

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

// Slice do índice 1 ao 3 (não inclui 3)

let slice: &amp;[i32] = &amp;numeros[1..3];

println!(&quot;{:?}&quot;, slice); // [20, 30]

// Do início até índice 2

let inicio = &amp;numeros[..2];

println!(&quot;{:?}&quot;, inicio); // [10, 20]

// Do índice 2 até o final

let fim = &amp;numeros[2..];

println!(&quot;{:?}&quot;, fim); // [30, 40, 50]

// Slice inteiro

let tudo = &amp;numeros[..];

println!(&quot;{:?}&quot;, tudo); // [10, 20, 30, 40, 50]

}</code></pre>

<p>A anotação de tipo <code>&amp;[i32]</code> 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.</p>

<h3>String Slices</h3>

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

<pre><code class="language-rust">fn main() {

let mensagem = &quot;Olá, Rust!&quot;;

let tipo: &amp;str = mensagem; // Automaticamente um slice

// Extraindo parte da string

let parte = &amp;mensagem[0..3];

println!(&quot;{}&quot;, parte); // &quot;Olá&quot;

// Função que aceita um slice de string

processar_texto(&amp;mensagem);

processar_texto(&quot;String literal&quot;);

}

fn processar_texto(texto: &amp;str) {

println!(&quot;Recebido: {}&quot;, texto);

}</code></pre>

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

<h2>O Sistema de Empréstimo e Segurança</h2>

<h3>Garantias de Segurança</h3>

<p>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 <strong>dangling pointers</strong> — um problema clássico de linguagens como C:</p>

<pre><code class="language-rust">fn main() {

let mut vetor = vec![1, 2, 3, 4, 5];

let slice = &amp;vetor[1..3]; // Empréstimo imutável

// Isto causaria erro de compilação:

// vetor.push(6); // Não pode modificar enquanto slice existe

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

// Após sair do escopo de &#039;slice&#039;, posso modificar novamente

vetor.push(6);

println!(&quot;{:?}&quot;, vetor);

}</code></pre>

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

<pre><code class="language-rust">fn main() {

let mut vetor = vec![10, 20, 30];

let slice_mut = &amp;mut vetor[0..2];

slice_mut[0] = 15; // Modificação via slice mutável

println!(&quot;{:?}&quot;, vetor); // [15, 20, 30]

}</code></pre>

<h2>Padrões Práticos com Slices</h2>

<h3>Funções que Trabalham com Partes de Dados</h3>

<p>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:</p>

<pre><code class="language-rust">fn soma_slice(numeros: &amp;[i32]) -&gt; i32 {

numeros.iter().sum()

}

fn maior_elemento(slice: &amp;[i32]) -&gt; Option&lt;i32&gt; {

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!(&quot;Soma: {}&quot;, soma_slice(&amp;arr));

// Funciona com vetores (convertidos para slices)

println!(&quot;Soma: {}&quot;, soma_slice(&amp;vetor));

// Funciona com partes específicas

println!(&quot;Maior: {:?}&quot;, maior_elemento(&amp;arr[1..4]));

}</code></pre>

<h3>Iteração Eficiente</h3>

<p>Slices trabalham perfeitamente com iteradores, sem necessidade de copiar dados:</p>

<pre><code class="language-rust">fn filtrar_pares(numeros: &amp;[i32]) -&gt; Vec&lt;i32&gt; {

numeros

.iter()

.filter(|n| *n % 2 == 0)

.copied()

.collect()

}

fn main() {

let dados = vec![1, 2, 3, 4, 5, 6];

let pares = filtrar_pares(&amp;dados);

println!(&quot;{:?}&quot;, pares); // [2, 4, 6]

}</code></pre>

<h2>Conclusão</h2>

<p>Slices são um pilar do Rust moderno. Eles oferecem três benefícios fundamentais que você deve internalizar: <strong>(1) segurança de memória</strong> garantida pelo compilador através do sistema de empréstimo; <strong>(2) eficiência</strong> permitindo trabalhar com partes de dados sem copiar ou alocar; e <strong>(3) flexibilidade</strong> 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.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://doc.rust-lang.org/book/ch04-03-slices.html" target="_blank" rel="noopener noreferrer">The Rust Book - Understanding Ownership</a></li>

<li><a href="https://doc.rust-lang.org/rust-by-example/primitives/array.html" target="_blank" rel="noopener noreferrer">Rust by Example - Slices</a></li>

<li><a href="https://doc.rust-lang.org/std/primitive.slice.html" target="_blank" rel="noopener noreferrer">Rust Standard Library Documentation - slice</a></li>

<li><a href="https://github.com/rust-lang/rustlings" target="_blank" rel="noopener noreferrer">Rustlings - Exercises for Learning Rust</a></li>

</ul>

Comentários

Mais em Rust

Generics em Rust: Funções e Structs Parametrizadas por Tipo: Do Básico ao Avançado
Generics em Rust: Funções e Structs Parametrizadas por Tipo: Do Básico ao Avançado

Introdução aos Generics em Rust Generics são um dos pilares da programação mo...

Como Usar O Operador ? em Rust: Propagação de Erros Elegante em Produção
Como Usar O Operador ? em Rust: Propagação de Erros Elegante em Produção

O Operador ? em Rust: Propagação de Erros Elegante O que é o Operador ? O ope...

Como Usar Borrowing e Referências em Rust: & e &mut na Prática em Produção
Como Usar Borrowing e Referências em Rust: & e &mut na Prática em Produção

Entendendo o Sistema de Ownership e Borrowing O Rust resolve o problema de ge...