Rust

Closures em Rust: Fn, FnMut e FnOnce na Prática na Prática

7 min de leitura

Closures em Rust: Fn, FnMut e FnOnce na Prática na Prática

O que são Closures em Rust? Closures são funções anônimas que podem capturar variáveis do escopo ao seu redor. Diferente de funções regulares, closures mantêm referências ou propriedade das variáveis externas, permitindo um padrão funcional poderoso. Em Rust, o tipo de closure é determinado automaticamente pelo compilador baseado em como ele usa as variáveis capturadas. A distinção entre , e é fundamental para entender como closures interagem com a memória e o ownership do Rust. O sistema de traits de closures foi projetado para oferecer máxima flexibilidade mantendo segurança de memória. Quando você cria um closure, o compilador analisa quais variáveis são capturadas e como são usadas, determinando automaticamente qual trait implementar. Esta análise acontece em tempo de compilação, não em runtime, garantindo zero overhead e máxima otimização. Entendendo os Três Traits: Fn, FnMut e FnOnce Fn — Apenas Leitura é o trait mais restritivo. Closures que implementam podem ser chamados múltiplas vezes e apenas leem as variáveis capturadas sem

<h2>O que são Closures em Rust?</h2>

<p>Closures são funções anônimas que podem capturar variáveis do escopo ao seu redor. Diferente de funções regulares, closures mantêm referências ou propriedade das variáveis externas, permitindo um padrão funcional poderoso. Em Rust, o tipo de closure é determinado automaticamente pelo compilador baseado em como ele usa as variáveis capturadas. A distinção entre <code>Fn</code>, <code>FnMut</code> e <code>FnOnce</code> é fundamental para entender como closures interagem com a memória e o ownership do Rust.</p>

<p>O sistema de traits de closures foi projetado para oferecer máxima flexibilidade mantendo segurança de memória. Quando você cria um closure, o compilador analisa quais variáveis são capturadas e como são usadas, determinando automaticamente qual trait implementar. Esta análise acontece em tempo de compilação, não em runtime, garantindo zero overhead e máxima otimização.</p>

<h2>Entendendo os Três Traits: Fn, FnMut e FnOnce</h2>

<h3>Fn — Apenas Leitura</h3>

<p><code>Fn</code> é o trait mais restritivo. Closures que implementam <code>Fn</code> podem ser chamados múltiplas vezes e apenas leem as variáveis capturadas sem modificá-las ou consumi-las. O closure recebe referências imutáveis (<code>&amp;T</code>) das variáveis externas.</p>

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

let multiplier = 5;

let multiply = |x| x * multiplier; // Captura multiplier como &amp;i32

println!(&quot;{}&quot;, multiply(3)); // 15

println!(&quot;{}&quot;, multiply(4)); // 20

// Pode ser chamado infinitas vezes

let result: Vec&lt;i32&gt; = vec![1, 2, 3]

.iter()

.map(|&amp;x| multiply(x))

.collect();

}</code></pre>

<h3>FnMut — Leitura e Escrita</h3>

<p><code>FnMut</code> permite que o closure modifique as variáveis capturadas, mas não as consome. Recebe referências mutáveis (<code>&amp;mut T</code>). Pode ser chamado múltiplas vezes, mas não simultaneamente em diferentes threads de forma segura.</p>

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

let mut counter = 0;

let mut increment = || {

counter += 1; // Modifica counter

counter

};

println!(&quot;{}&quot;, increment()); // 1

println!(&quot;{}&quot;, increment()); // 2

println!(&quot;{}&quot;, increment()); // 3

// Passando FnMut para uma função

apply_twice(&amp;mut increment);

}

fn apply_twice&lt;F&gt;(f: &amp;mut F)

where

F: FnMut(),

{

f();

f();

}</code></pre>

<h3>FnOnce — Consumo Total</h3>

<p><code>FnOnce</code> consome as variáveis capturadas, transferindo sua propriedade para dentro do closure. Pode ser chamado apenas uma vez. Use quando o closure precisa tomar posse dos dados capturados.</p>

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

let name = String::from(&quot;Rust&quot;);

let greeting = || {

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

};

greeting(); // Primeira chamada

// greeting(); // Erro: já foi consumido

// Exemplo com uma thread

let value = String::from(&quot;dados importantes&quot;);

std::thread::spawn(move || {

println!(&quot;Thread usa: {}&quot;, value); // move transfere propriedade

}).join().unwrap();

// println!(&quot;{}&quot;, value); // Erro: value foi movido

}</code></pre>

<h2>Hierarquia e Coerção de Traits</h2>

<p>Existe uma relação hierárquica entre estes traits: todo <code>Fn</code> é também <code>FnMut</code>, e todo <code>FnMut</code> é também <code>FnOnce</code>. Isto significa que você pode passar um <code>Fn</code> onde <code>FnMut</code> ou <code>FnOnce</code> é esperado, mas não o contrário.</p>

<pre><code class="language-rust">fn execute_fn&lt;F&gt;(f: F)

where

F: Fn(i32) -&gt; i32,

{

println!(&quot;Resultado: {}&quot;, f(5));

}

fn execute_fn_mut&lt;F&gt;(mut f: F)

where

F: FnMut(i32) -&gt; i32,

{

println!(&quot;Primeiro: {}&quot;, f(5));

println!(&quot;Segundo: {}&quot;, f(10));

}

fn main() {

let add_three = |x| x + 3; // Implementa Fn

execute_fn(add_three); // OK: Fn pode ser usado como Fn

execute_fn_mut(add_three); // OK: Fn pode ser usado como FnMut

let mut sum = 0;

let adder = |x| {

sum += x; // Implementa FnMut

sum

};

execute_fn_mut(adder); // OK: FnMut pode ser usado como FnMut

// execute_fn(adder); // Erro: FnMut não pode ser Fn

}</code></pre>

<h2>Casos Práticos de Uso</h2>

<p>Closures são essenciais para padrões funcionais e callbacks em Rust. A escolha do trait correto impacta a flexibilidade e performance do seu código. Iteradores usam closures extensivamente: <code>map</code> requer <code>Fn</code>, enquanto <code>for_each</code> permite <code>FnMut</code>.</p>

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

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

let multiplier = 10;

// map usa Fn — não modifica estado externo

let doubled: Vec&lt;i32&gt; = numbers

.iter()

.map(|x| x * multiplier)

.collect();

let mut total = 0;

// for_each usa FnMut — pode modificar total

numbers.iter().for_each(|&amp;x| {

total += x;

});

println!(&quot;Somados: {}&quot;, total); // 15

// Processando Strings com FnOnce

let process_data = |data: String| {

data.to_uppercase()

};

let result = process_data(String::from(&quot;rust&quot;));

println!(&quot;{}&quot;, result); // RUST

}</code></pre>

<h2>Conclusão</h2>

<p>Os três traits de closures em Rust — <code>Fn</code>, <code>FnMut</code> e <code>FnOnce</code> — refletem diferentes níveis de controle sobre dados capturados. <strong>Primeiro aprendizado</strong>: escolha <code>Fn</code> quando apenas ler variáveis; <code>FnMut</code> quando modificá-las; <code>FnOnce</code> quando consumir. <strong>Segundo aprendizado</strong>: esta hierarquia não é apenas sintaxe, mas um reflexo direto do sistema de ownership, oferecendo segurança em tempo de compilação. <strong>Terceiro aprendizado</strong>: dominar estes conceitos abre as portas para programação funcional elegante em Rust, essencial para iteradores, threads e APIs modernas.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://doc.rust-lang.org/book/ch13-01-closures.html" target="_blank" rel="noopener noreferrer">The Rust Book — Closures</a></li>

<li><a href="https://doc.rust-lang.org/rust-by-example/fn/closures.html" target="_blank" rel="noopener noreferrer">Rust By Example — Closures</a></li>

<li><a href="https://doc.rust-lang.org/std/ops/trait.Fn.html" target="_blank" rel="noopener noreferrer">Fn, FnMut, and FnOnce — Official Docs</a></li>

<li><a href="https://www.oreilly.com/library/view/programming-rust-2nd/9781492052586/" target="_blank" rel="noopener noreferrer">Programming Rust — Jim Blandy &amp; Jason Orendorff</a></li>

<li><a href="https://rust-lang.github.io/api-guidelines/type-safety.html" target="_blank" rel="noopener noreferrer">Rust Design Patterns — Closures</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...

Boas Práticas de FFI em Rust: Interoperabilidade com C e Outras Linguagens para Times Ágeis
Boas Práticas de FFI em Rust: Interoperabilidade com C e Outras Linguagens para Times Ágeis

FFI em Rust: Interoperabilidade com C e Outras Linguagens FFI (Foreign Functi...

Drop e o Ciclo de Vida de Recursos em Rust: Do Básico ao Avançado
Drop e o Ciclo de Vida de Recursos em Rust: Do Básico ao Avançado

O Trait Drop e o Ciclo de Vida de Recursos em Rust Rust gerencia memória sem...