Skip to main content

|| | closure | Barra vertical dupla | Rust

  • || | closure | Barra vertical dupla | Rust

O que é uma closure

Uma closure é uma função anônima que pode:

  • ser atribuída a variáveis
  • ser passada como argumento
  • capturar valores do escopo onde foi definida

Em Rust, closures são definidas usando barras verticais (| |).


Para que serve ||

O || indica a lista de parâmetros da closure.

  • || → nenhum parâmetro
  • |x| → um parâmetro
  • |x, y| → dois parâmetros

Exemplo sem parâmetros:

let f = || {
println!("Olá");
};

Exemplo com parâmetros:

let soma = |a: i32, b: i32| {
a + b
};

O papel do || é equivalente ao () em funções tradicionais.


Comparação com funções tradicionais

Função normal:

fn soma(a: i32, b: i32) -> i32 {
a + b
}

Closure equivalente:

let soma = |a: i32, b: i32| a + b;

Diferença fundamental:

  • funções não capturam variáveis externas
  • closures podem capturar valores do escopo

Captura de variáveis pelo compilador

Por padrão, o compilador Rust escolhe a forma menos restritiva de captura:

  • referência imutável (&T)
  • referência mutável (&mut T)
  • movimento (T), apenas se necessário

Exemplo:

fn main() {
let x = 5;

let f = || {
println!("{}", x);
};

f();
}

Nesse caso:

  • x é capturado por empréstimo
  • x continua válido fora da closure

Comportamento do move

Ao adicionar move, a closure passa a capturar todos os valores por movimento, independentemente de necessidade.

Exemplo:

fn main() {
let x = 5;

let f = move || {
println!("{}", x);
};

f();
}

Aqui:

  • x é movido para dentro da closure
  • a closure se torna dona do valor

Relação entre move e ||

Os dois elementos têm papéis distintos:

  • || define os parâmetros da closure
  • move define como as variáveis externas são capturadas

Exemplo completo:

let f = move || {
println!("Executando closure");
};

Tipos que implementam Copy

Quando o valor capturado implementa Copy, o move realiza apenas uma cópia.

Exemplo:

fn main() {
let x = 10;

let f = move || {
println!("{}", x);
};

println!("{}", x);
f();
}

Tipos que não implementam Copy

Para tipos como String ou Vec<T>, o move transfere a posse definitivamente.

Exemplo:

fn main() {
let s = String::from("Rust");

let f = move || {
println!("{}", s);
};

// uso de s aqui causaria erro
f();
}

Uso com threads

Ao criar threads, a closure pode sobreviver ao escopo atual, tornando o move obrigatório.

Exemplo:

use std::thread;

fn main() {
let x = 5;

thread::spawn(move || {
println!("{}", x);
});
}

Nesse caso:

  • referências ao stack local seriam inválidas
  • o move garante segurança de memória

move em closures retornadas

Quando uma função retorna uma closure, os valores capturados não podem depender do escopo local da função.

Exemplo:

fn criar_closure() -> impl Fn() {
let x = 42;

move || {
println!("{}", x);
}
}

Pontos importantes

  • || define os parâmetros da closure
  • move controla a posse dos valores capturados
  • a captura ocorre no momento da criação da closure
  • move não altera a lógica interna da closure
  • closures são diferentes de funções tradicionais