|| | 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éstimoxcontinua 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 closuremovedefine 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
movegarante 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 closuremovecontrola a posse dos valores capturados- a captura ocorre no momento da criação da closure
movenão altera a lógica interna da closure- closures são diferentes de funções tradicionais