Follow

Follow

Resumo: Closure no Rust

Henry Barreto's photo
Henry Barreto
·Jul 30, 2021·

4 min read

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
}

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 || { 
   |     ^^^^^^^

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.

Did you find this article valuable?

Support Henry Barreto by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this