Semântica de Movimento
Uma atribuição transferirá o ownership entre variáveis:
fn main() { let s1: String = String::from("Olá!"); let s2: String = s1; println!("s2: {s2}"); // println!("s1: {s1}"); }
- A atribuição de
s1as2transfere o ownership. - Quando
s1sai do escopo, nada acontece: ele não tem ownership. - Quando
s2sai do escopo, os dados da string são liberados.
Antes de mover para s2:
Depois de mover para s2:
Quando você passa um valor para uma função, o valor é atribuído ao parâmetro da função. Isso transfere a ownership:
fn say_hello(name: String) { println!("Olá {name}") } fn main() { let name = String::from("Alice"); say_hello(name); // say_hello(name); }
-
Mencione que isso é o oposto dos defaults (padrões) em C++, que copia por valor, a menos que você use
std::move(e seu construtor esteja definido!). -
Apenas o ownership é movido. A geração de código de máquina para manipular os dados é uma questão de otimização, e essas cópias são agressivamente otimizadas.
-
Valores simples (tais como inteiros) podem ser marcados como
Copy(cópia) (veja slides mais adiante). -
No Rust, clones são explícitos (utilizando-se
clone).
No exemplo say_hello:
- Com a primeira chamada para
diga_ola,maindesiste da ownership denome. Depois disso,nomenão pode mais ser usado dentro demain. - A memória do heap alocada para
nameserá liberada no final da funçãosay_hello. mainpode manter a ownership se passarnomecomo uma referência (&name) e sesay_helloaceitar uma referência como um parâmetro.- Alternativamente,
mainpode passar um clone denomena primeira chamada (name.clone()). - Rust torna mais difícil a criação de cópias inadvertidamente do que o C++, tornando padrão a semântica de movimento e forçando os programadores a tornar os clones explícitos.
Mais para Explorar
Cópias Defensivas em C++ Moderno
O C++ moderno resolve isso de maneira diferente:
std::string s1 = "Cpp";
std::string s2 = s1; // Duplica os dados em s1.
- Os dados de
s1no heap são duplicados es2obtém sua própria cópia independente. - Quando
s1es2saem de escopo, cada um libera sua própria memória.
Antes da atribuição por cópia:
Após atribuição por cópia:
Pontos chave:
-
O C++ fez uma escolha ligeiramente diferente do Rust. Como
=copia dados, os dados da string devem ser clonados. Caso contrário, obteríamos uma dupla liberação quando qualquer string saísse de escopo. -
O C++ também possui
std::move, que é usado para indicar quando um valor pode ser movido. Se o exemplo fosses2 = std::move(s1), nenhuma alocação de heap seria feita. Após a movimentação,s1estaria em um estado válido, mas não especificado. Diferentemente do Rust, o programador pode continuar usandos1. -
Diferentemente do Rust,
=em C++ pode executar código arbitrário conforme determinado pelo tipo que está sendo copiado ou movido.