Funções como argumentos em Rust: 3 maneiras de passar uma função

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Função como argumento em Rust: do iniciante ao profissional

\n\n

Você já escreveu em Python ou JavaScript e passou uma função para outra função com facilidade? E depois mudou para Rust e tropeçou em terreno plano? Conhecido. A pergunta "How do you pass a Rust function as a parameter?" é uma das mais populares no Stack Overflow. E não é à toa: o sistema de tipos e ownership do Rust adiciona várias camadas de complexidade, mas também abre novas possibilidades.

\n\n

Neste artigo, vamos explorar três abordagens principais: ponteiros para funções (fn), closures (closures) e traits. Você aprenderá quando usar cada uma e deixará de ter medo do estilo funcional em Rust.

\n\n

Por que isso é importante?

\n

Saber passar funções como parâmetros não é apenas um truque acadêmico. É a base para:

\n
    \n
  • Criar bibliotecas flexíveis e reutilizáveis (por exemplo, iteradores, manipuladores de eventos);
  • \n
  • Implementar os padrões "Strategy" e "Observer";
  • \n
  • Escrever código conciso e expressivo sem montes de if-else.
  • \n
\n

Em Rust, ao contrário de linguagens dinâmicas, o compilador verifica rigorosamente os tipos e os tempos de vida. Portanto, você precisa entender claramente o que está passando: apenas um endereço de função, uma closure com captura de contexto ou algo mais complexo.

\n\n

Abordagem 1: Ponteiro para função (Function Pointer) — fn

\n

A maneira mais simples e direta. Se sua função não captura variáveis do ambiente externo (ela é "pura"), você pode usar o tipo fn. Observe: fn é um tipo, enquanto Fn, FnMut, FnOnce são traits. Não confunda!

\n\n
fn add_one(x: i32) -> i32 {\n    x + 1\n}\n\n// Função que recebe outra função\nfn apply_twice(f: fn(i32) -> i32, value: i32) -> i32 {\n    f(f(value))\n}\n\nfn main() {\n    let result = apply_twice(add_one, 5);\n    println!("Resultado: {}", result); // 7\n}
\n\n

Quando usar? Quando a função é conhecida em tempo de compilação, não captura variáveis e você não precisa da performance das closures. Ponteiros para funções são uma zero-cost abstraction, não exigem alocações.

\n\n

Limitações de fn

\n

A principal desvantagem: você não pode passar uma closure que captura variáveis. O código a seguir não compilará:

\n\n
fn main() {\n    let y = 10;\n    // Closure captura y\n    let closure = |x: i32| -> i32 { x + y };\n    // Erro! expected fn item, found closure\n    // let result = apply_twice(closure, 5);\n}
\n\n

Para isso, são necessários os traits Fn, FnMut, FnOnce.

\n\n

Abordagem 2: Closures e os traits Fn, FnMut, FnOnce

\n

Rust é uma linguagem de sistema e precisa saber o que acontece com a memória. Closures podem capturar variáveis de três maneiras:

\n
    \n
  • Por referência (&T) — trait Fn. Pode ser chamada quantas vezes quiser, sem modificar os dados capturados.
  • \n
  • Por referência mutável (&mut T) — trait FnMut. Pode ser chamada várias vezes, mas os dados podem ser modificados.
  • \n
  • Por valor (T) — trait FnOnce. Só pode ser chamada uma vez, após o que a closure é "consumida".
  • \n
\n\n

Exemplo com Fn

\n
fn apply_twice_fn<F>(f: F, value: i32) -> i32\nwhere\n    F: Fn(i32) -> i32,\n{\n    f(f(value))\n}\n\nfn main() {\n    let y = 10;\n    let closure = |x| x + y; // captura y por referência\n    let result = apply_twice_fn(closure, 5);\n    println!("Fn: {}", result); // 25\n}
\n\n

Exemplo com FnMut

\n
fn apply_twice_mut<F>(mut f: F, value: i32) -> i32\nwhere\n    F: FnMut(i32) -> i32,\n{\n    f(f(value))\n}\n\nfn main() {\n    let mut counter = 0;\n    let mut closure = |x: i32| -> i32 {\n                

Blogs

Book Recommendations