Rust

Guia Completo de Iteradores Customizados: Implementando o Trait Iterator

9 min de leitura

Guia Completo de Iteradores Customizados: Implementando o Trait Iterator

Por que Implementar Iteradores Customizados? Iteradores são fundamentais em linguagens funcionais e modernas como Rust, Python e JavaScript. Em Rust, o trait permite criar loops eficientes e expressivos sobre estruturas de dados personalizadas. Quando você implementa o trait , seu tipo se integra perfeitamente com o ecossistema Rust, funcionando com , métodos como , e . Isso não é apenas conveniência sintática — é performance garantida com zero-cost abstractions. Neste artigo, vamos construir iteradores reais, entender o trait por dentro e aprender quando e como usá-los. Você sairá daqui sabendo implementar iteradores para suas próprias estruturas de dados. Entendendo o Trait Iterator A Anatomia do Trait O trait em Rust é simples mas poderoso. Ele requer apenas um método obrigatório: , que retorna . O tipo associado define o tipo de elemento que o iterador produz. > A elegância aqui: todos os métodos convenientes derivam de . Implementar um método bem feito oferece dezenas de operações de graça. Implementação Básica

<h2>Por que Implementar Iteradores Customizados?</h2>

<p>Iteradores são fundamentais em linguagens funcionais e modernas como Rust, Python e JavaScript. Em Rust, o trait <code>Iterator</code> permite criar loops eficientes e expressivos sobre estruturas de dados personalizadas. Quando você implementa o trait <code>Iterator</code>, seu tipo se integra perfeitamente com o ecossistema Rust, funcionando com <code>for</code>, métodos como <code>map()</code>, <code>filter()</code> e <code>collect()</code>. Isso não é apenas conveniência sintática — é performance garantida com zero-cost abstractions.</p>

<p>Neste artigo, vamos construir iteradores reais, entender o trait <code>Iterator</code> por dentro e aprender quando e como usá-los. Você sairá daqui sabendo implementar iteradores para suas próprias estruturas de dados.</p>

<h2>Entendendo o Trait Iterator</h2>

<h3>A Anatomia do Trait</h3>

<p>O trait <code>Iterator</code> em Rust é simples mas poderoso. Ele requer apenas um método obrigatório: <code>next()</code>, que retorna <code>Option&lt;Self::Item&gt;</code>. O tipo associado <code>Item</code> define o tipo de elemento que o iterador produz.</p>

<pre><code class="language-rust">pub trait Iterator {

type Item;

fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt;;

// Métodos padrão (map, filter, collect, etc.)

// todos construídos sobre next()

}</code></pre>

<blockquote><p>A elegância aqui: todos os métodos convenientes derivam de <code>next()</code>. Implementar um método bem feito oferece dezenas de operações de graça.</p></blockquote>

<h3>Implementação Básica</h3>

<p>Vamos criar um iterador para uma sequência de números pares:</p>

<pre><code class="language-rust">struct ContadorPares {

inicio: u32,

fim: u32,

}

impl ContadorPares {

fn new(inicio: u32, fim: u32) -&gt; Self {

ContadorPares { inicio, fim }

}

}

impl Iterator for ContadorPares {

type Item = u32;

fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {

if self.inicio &lt; self.fim {

if self.inicio % 2 == 0 {

let resultado = self.inicio;

self.inicio += 1;

Some(resultado)

} else {

self.inicio += 1;

self.next() // recursão para pular ímpares

}

} else {

None

}

}

}

fn main() {

let contador = ContadorPares::new(0, 10);

for num in contador {

println!(&quot;{}&quot;, num); // 0, 2, 4, 6, 8

}

}</code></pre>

<p>Note que o loop <code>for</code> funciona automaticamente. Rust reconhece que <code>ContadorPares</code> implementa <code>Iterator</code> e desaçúcar o loop para chamadas sucessivas de <code>next()</code>.</p>

<h2>Iteradores em Estruturas de Dados</h2>

<h3>Iterando Sobre Estruturas Personalizadas</h3>

<p>Frequentemente você quer iterar sobre uma coleção própria. Considere uma lista ligada customizada:</p>

<pre><code class="language-rust">struct No&lt;T&gt; {

valor: T,

proximo: Option&lt;Box&lt;No&lt;T&gt;&gt;&gt;,

}

struct ListaLigada&lt;T&gt; {

cabeca: Option&lt;Box&lt;No&lt;T&gt;&gt;&gt;,

}

struct IteradorLista&lt;T&gt; {

no_atual: Option&lt;&amp;&#039;a Box&lt;No&lt;T&gt;&gt;&gt;,

}

impl&lt;T&gt; ListaLigada&lt;T&gt; {

fn iter(&amp;self) -&gt; IteradorLista&lt;T&gt; {

IteradorLista {

no_atual: self.cabeca.as_ref(),

}

}

}

impl&lt;&#039;a, T&gt; Iterator for IteradorLista&lt;&#039;a, T&gt; {

type Item = &amp;&#039;a T;

fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {

self.no_atual.map(|no| {

self.no_atual = no.proximo.as_ref();

&amp;no.valor

})

}

}

fn main() {

let mut lista = ListaLigada { cabeca: None };

// Preenchimento da lista...

for valor in lista.iter() {

println!(&quot;{}&quot;, valor);

}

}</code></pre>

<p>Aqui usamos lifetimes (<code>&#039;a</code>) para garantir que as referências retornadas pelo iterador vivem o tempo adequado. O método <code>map()</code> em <code>Option</code> é elegante: se houver um nó, retorna seu valor e avança; caso contrário, retorna <code>None</code> automaticamente.</p>

<h3>Iteradores Consumidores vs. Emprestados</h3>

<p>Há três padrões comuns:</p>

<ul>

<li><strong><code>iter()</code></strong>: retorna referências (não-mutáveis), a coleção permanece intacta</li>

<li><strong><code>iter_mut()</code></strong>: retorna referências mutáveis, permite modificação</li>

<li><strong><code>into_iter()</code></strong>: consome a coleção, transfere ownership</li>

</ul>

<pre><code class="language-rust">impl&lt;T&gt; ListaLigada&lt;T&gt; {

fn iter_mut(&amp;mut self) -&gt; IteradorListaMut&lt;T&gt; {

IteradorListaMut {

no_atual: self.cabeca.as_mut(),

}

}

}

impl&lt;&#039;a, T&gt; Iterator for IteradorListaMut&lt;&#039;a, T&gt; {

type Item = &amp;&#039;a mut T;

fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {

self.no_atual.take().map(|no| {

self.no_atual = no.proximo.as_mut();

&amp;mut no.valor

})

}

}</code></pre>

<h2>Operações Encadeadas e Performance</h2>

<h3>Composição de Métodos</h3>

<p>Um dos superpoderes do trait <code>Iterator</code> é a capacidade de encadear operações de forma limpa e <strong>eficiente</strong>. Veja:</p>

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

let pares = ContadorPares::new(0, 20)

.filter( | n | n % 3 != 0) // Remove múltiplos de 3 .map(|n| n * 2) // Dobra cada número

.take(5); // Pega apenas 5 primeiros

let resultado: Vec&lt;u32&gt; = pares.collect();

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

}</code></pre>

<p>Isso não aloca intermediários como vetores temporários — é <strong>lazy evaluation</strong>. O iterador computaciona valores sob demanda, de forma eficiente em memória e CPU.</p>

<h3>Implementando Métodos Customizados</h3>

<p>Você pode adicionar métodos específicos ao seu iterador:</p>

<pre><code class="language-rust">trait IteratorExt: Iterator {

fn dobrar(self) -&gt; Map&lt;Self, fn(Self::Item) -&gt; Self::Item&gt;

where

Self: Sized,

{

self.map(|x: Self::Item| x * 2)

}

}

impl&lt;I: Iterator&gt; IteradorExt for I {}

// Uso:

let resultado: Vec&lt;u32&gt; = ContadorPares::new(1, 5)

.dobrar()

.collect();</code></pre>

<p>Aqui estendemos qualquer iterador com novos comportamentos sem modificar sua definição — isso é <strong>composição horizontal</strong> em ação.</p>

<h2>Conclusão</h2>

<p>Implementar o trait <code>Iterator</code> em Rust oferece três ganhos centrais:</p>

<ol>

<li><strong>Integração ecosistema</strong>: seu tipo funciona com <code>for</code>, <code>map()</code>, <code>filter()</code>, <code>collect()</code> — toda a linguagem passa a trabalhar com seus dados naturalmente.</li>

</ol>

<ol>

<li><strong>Eficiência garantida</strong>: lazy evaluation, zero-copy quando apropriado, e compilação otimizada pelo LLVM garantem performance em produção.</li>

</ol>

<ol>

<li><strong>Clareza expressiva</strong>: iteradores customizados transformam lógica complexa de percurso em código declarativo e legível, reduzindo bugs.</li>

</ol>

<p>O investimento em dominar <code>Iterator</code> se paga rapidamente. Toda estrutura de dados relevante em Rust implementa este trait — aprender a construir iteradores é aprender a linguagem na profundidade certa.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://doc.rust-lang.org/book/ch13-02-iterators.html" target="_blank" rel="noopener noreferrer">The Rust Book - Iterators</a></li>

<li><a href="https://doc.rust-lang.org/std/iter/trait.Iterator.html" target="_blank" rel="noopener noreferrer">Rust std::iter::Iterator Documentation</a></li>

<li><a href="https://docs.rs/itertools/latest/itertools/" target="_blank" rel="noopener noreferrer">Itertools Crate — Extensões de Iteradores</a></li>

<li><a href="https://zsiciarz.github.io/24daysofrust/book/vol2/day1.html" target="_blank" rel="noopener noreferrer">24 Days of Rust — Iterators</a></li>

<li><a href="https://doc.rust-lang.org/rust-by-example/trait/iter.html" target="_blank" rel="noopener noreferrer">Rust by Example — Iterators</a></li>

</ul>

Comentários

Mais em Rust

O que Todo Dev Deve Saber sobre Async e Await em Rust: Introdução à Programação Assíncrona
O que Todo Dev Deve Saber sobre Async e Await em Rust: Introdução à Programação Assíncrona

Entendendo Async e Await em Rust A programação assíncrona permite que seu pro...

O que Todo Dev Deve Saber sobre Deploy de Aplicações Rust em VPS com Docker e Nginx
O que Todo Dev Deve Saber sobre Deploy de Aplicações Rust em VPS com Docker e Nginx

Preparando sua Aplicação Rust para Produção Antes de fazer deploy, sua aplica...

Boas Práticas de Features e Compilação Condicional em Rust com Cargo para Times Ágeis
Boas Práticas de Features e Compilação Condicional em Rust com Cargo para Times Ágeis

Features em Rust: O Que São e Por Que Importam As features (características)...