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
s1
as2
transfere o ownership. - Quando
s1
sai do escopo, nada acontece: ele nĂŁo tem ownership. - Quando
s2
sai 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
,main
desiste da ownership denome
. Depois disso,nome
nĂŁo pode mais ser usado dentro demain
. - A memĂłria do heap alocada para
name
serå liberada no final da funçãosay_hello
. main
pode manter a ownership se passarnome
como uma referĂȘncia (&name
) e sesay_hello
aceitar uma referĂȘncia como um parĂąmetro.- Alternativamente,
main
pode passar um clone denome
na 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
s1
no heap sĂŁo duplicados es2
obtém sua própria cópia independente. - Quando
s1
es2
saem 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,s1
estaria 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.