move | Rust
- move | Rust
Palavra reservada move em Rust
O que é move
A palavra reservada move em Rust é usada principalmente em closures para indicar que os valores capturados do escopo externo devem ter sua posse transferida para dentro da closure, em vez de serem apenas emprestados por referência.
Motivação
Rust possui um sistema rígido de ownership e borrowing para garantir segurança de memória em tempo de compilação.
Quando uma closure pode viver mais tempo que o escopo onde foi criada (como em threads), ela não pode depender de referências que podem deixar de existir.
O move resolve isso tornando a closure dona dos valores capturados.
Comportamento sem move
Por padrão, closures capturam variáveis do escopo externo da forma menos restritiva possível:
- 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é emprestado- a variável continua válida fora da closure
Comportamento com move
Ao usar move, a closure passa a capturar os valores por movimento, tornando-se dona deles.
Exemplo:
fn main() {
let x = 5;
let f = move || {
println!("{}", x);
};
f();
}
Nesse cenário:
xé movido para dentro da closure- a variável original não pode mais ser usada, a menos que implemente
Copy
Relação com tipos Copy
Para tipos que implementam o trait Copy (como i32, bool, f64), o move realiza uma cópia, não invalidando a variável original.
Exemplo:
fn main() {
let x = 10;
let f = move || {
println!("{}", x);
};
println!("{}", x); // válido
f();
}
Tipos que não implementam Copy
Para tipos como String, Vec<T> e structs em geral, o move transfere a posse de forma definitiva.
Exemplo:
fn main() {
let s = String::from("Rust");
let f = move || {
println!("{}", s);
};
// println!("{}", s); // erro: valor movido
f();
}
Uso com threads
Em contextos concorrentes, como criação de threads, o uso de move é obrigatório.
Exemplo:
use std::thread;
fn main() {
let x = 5;
thread::spawn(move || {
println!("{}", x);
});
}
Motivo:
- a thread pode sobreviver ao escopo atual
- referências ao stack local seriam inválidas
movegarante que os dados estejam disponíveis com segurança
move em closures retornadas
Quando uma função retorna uma closure, o move é necessário para garantir que os valores capturados não dependam do escopo local da função.
Exemplo:
fn criar_closure() -> impl Fn() {
let x = 42;
move || {
println!("{}", x);
}
}
Pontos importantes
moveafeta como a closure captura variáveis, não o tipo da closure- o movimento acontece no momento da criação da closure
- não altera a semântica interna do código, apenas a posse dos dados
- é exclusivo de closures, não se aplica a funções normais