Semántica de movimiento
Una asignación transferirá su ownership entre variables:
fn main() { let s1: String = String::from("Hello!"); let s2: String = s1; println!("s2: {s2}"); // println!("s1: {s1}"); }
- La asignación de
s1
as2
transfiere el ownership. - Cuando
s1
queda fuera del ámbito, no ocurre nada: no le pertenece nada. - Cuando
s2
sale del ámbito, los datos de la cadena se liberan.
Antes de mover a s2
:
Después de mover a s2
:
Cuando pasas un valor a una función, el valor se asigna al parámetro de la función. Esta acción transfiere el ownership:
fn say_hello(name: String) { println!("Hello {name}") } fn main() { let name = String::from("Alice"); say_hello(name); // say_hello(name); }
-
Menciona que es lo contrario de los valores predeterminados de C++, que se copian por valor, a menos que utilices
std::move
(y que el constructor de movimiento esté definido). -
Es únicamente el ownership el que se mueve. Si se genera algún código máquina para manipular los datos en sí, se trata de una cuestión de optimización, y esas copias se optimizan de forma agresiva.
-
Los valores simples (como los enteros) se pueden marcar como
Copy
(consulta las diapositivas posteriores). -
En Rust, la clonación es explícita (usando
clone
).
In the say_hello
example:
- Con la primera llamada a
say_hello
,main
deja de tener el ownership dename
. Después, ya no se podrá usarname
dentro demain
. - La memoria de heap asignada a
name
se liberará al final de la funciónsay_hello
. - main
podrá conservar el _ownership_ si pasa
namecomo referencia (
&name) y si
say_hello` acepta una referencia como parámetro. - Por otro lado,
main
puede pasar un clon dename
en la primera llamada (name.clone()
). - Rust hace que resulte más difícil que en C++ crear copias por error al definir la semántica de movimiento como predeterminada y al obligar a los programadores a clonar sólo de forma explícita.
More to Explore
Defensive Copies in Modern C++
La versión moderna de C++ soluciona este problema de forma diferente:
std::string s1 = "Cpp";
std::string s2 = s1; // Duplicate the data in s1.
- Los datos de la stack de
s1
se duplican ys2
obtiene su propia copia independiente. - Cuando
s1
ys2
salen del ámbito, cada uno libera su propia memoria.
Antes de la asignación de copias:
Después de la asignación de copia:
Puntos clave:
-
C++ has made a slightly different choice than Rust. Because
=
copies data, the string data has to be cloned. Otherwise we would get a double-free when either string goes out of scope. -
C++ also has
std::move
, which is used to indicate when a value may be moved from. If the example had beens2 = std::move(s1)
, no heap allocation would take place. After the move,s1
would be in a valid but unspecified state. Unlike Rust, the programmer is allowed to keep usings1
. -
Unlike Rust,
=
in C++ can run arbitrary code as determined by the type which is being copied or moved.