Skip to main content

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
  • move garante 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

  • move afeta 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