DEV Community

Cover image for Resumo: Closure no Rust
Henry Barreto
Henry Barreto

Posted on • Updated on

Resumo: Closure no Rust

O que é Closure?

O termo closure é bastante utilizado quando se comenta sobre programação funcional, podendo ser definido como, segundo MDN Web Docs, uma função que se "lembra" do ambiente — ou escopo léxico — em que ela foi criada.

Na obra, Functional Thinking: Paradigm Over Syntax, um closure é definido, em tradução livre, como uma função que carrega implicitamente as definições para todas as variáveis internamente referenciadas. Em outras palavras, a função "engloba" o contexto em volta das "coisas" que ela referencia.

Se essas definições não forem tão claras, como eu penso que não são, e se uma imagem fala por mil palavras, então um vídeo é um enciclopédia; O canal Código Fonte TV fez um ótimo sobre o assunto, que acredito ajudar bastante no entendimento do tema.

Julgo que um bom resumo seria definir um closure como "algo" que permite funções que vivem dentro de outras funções possam acessar o escopo da função mais externa. Confuso? Também! Vamos ver um exemplo.

fn soma_com_x(valor: i32) -> impl Fn() -> i32 {
    let x = 10; // Variável que pertence ao escopo mais externo
    // move aqui tem o papel de transferir o valor `x` para a closure
    move || {valor + x} // Closure
}

fn contatador() -> impl FnMut() -> i32 {
    // Como a função que é retorna altera o estado da função mais externa, é
    // necessário o uso do Trait FnMut
    let mut i = 0;
    move || { // Função anônima que actual como closure
        i = i + 1;
        i
    }
}

fn main() {
    let closure_soma_com_x = soma_com_x(5);
    let valor = closure(); // valor = 15
    println!("{}", valor); // 15
    let mut closure_contador = contatador(); // Retorna a closure
    // É necessário a mutabilidade pelo fato da alteração do estado interno
    closure_contador(); // i = 1
    closure_contador(); // i = 2
    closure_contador(); // i = 3
    closure_contador(); // i = 4
    println!("{}", closure_contador()); // i = 5
}
Enter fullscreen mode Exit fullscreen mode

Hands-On Functional Programming in Rust diz que um closure é um objeto que atua como uma função, implementando fn, Fn, FnMut ou FnOnce [...] os Trait citados são automaticamente implementados se permitido quando há a passagem de uma função ou um lambda. (modificado e traduzido).

Para utilizar uma função como parâmetro ou como tipo de retorno, função de alta ordem, é necessário o uso do Trait adequado para o propósito. Por exemplo: se a necessidade é um closure mais simples, para ser usada em single thread e que apenas consume as variáveis, o Trait Fn é suficiente; se há necessidade de alterar os valores internos, um FnMut é a opção correta, já se esse closure vai ser enviada para dentro de Threads, FnOnce, que permite a execução única dessa função, é a alterativa preferida.

Não se pode esquecer das palavras reservadas dyn e impl conforme a necessidade

Move

Conhecendo o Borrow Checker e Ownership do Rust e suas peculiaridades em relação a controle de memória, podemos dizer haver um detalhe não dito acima. Move converte qualquer variável capturada por referência ou referência mutável para valores que o closure "possui", Owned, permitindo o comportado visto. A saída do compilador quando o Move não é especificado é bem esclarecedora, como sempre.

error[E0373]: closure may outlive the current function, but it borrows `i`, which is owned by the current function
  --> src/main.rs:82:5
   |
82 |     || { 
   |     ^^ may outlive borrowed value `i`
83 |         i = i + 1;
   |             - `i` is borrowed here
   |
note: closure is returned here
  --> src/main.rs:80:20
   |
80 | fn contatador() -> impl FnMut() -> i32 {
   |                    ^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `i` (and any other referenced variables), use the `move` keyword
   |
82 |     move || { 
   |     ^^^^^^^
Enter fullscreen mode Exit fullscreen mode

Como curiosidade, Move e bastante utilizado com Threads, quando é necessário enviar esses dados da principal para as filhas; claro, há mais detalhes que não são tão relacionados com as closures, e não é o foco desse blog, mas que vale a pena se aprofundar.

É isso; um resuminho rápido, simples e direto ao ponto. Se for necessário maior aprofundamento no tema, deixo alguns links que usei para estudar sobre.

Agradeço pela leitura e fique a vontade para comentar, compartilhar ou se algo não estive correto, dá sugestões de melhoria, o que eu vou agradecer muito, já que esses blogs são mais para aprendizado mesmo.

Links relacionados

Top comments (2)

Collapse
 
kelvincesar profile image
Kelvin César de Andrade

Muito bom, obrigado pelo exemplo!

Collapse
 
henrybarreto profile image
Henry Barreto

Desponha, man. Tanto sempre ajudar.