6.2 Move Semantics, Cloning, and Copying
6.2.1 Move Semantics
Rust uses move semantics for types that manage resources like heap memory or file handles. When you assign such a type to another variable or pass it to a function, the ownership is moved.
fn main() { let s1 = String::from("hello"); let s2 = s1; // Move occurs // s1 is invalidated }
Move semantics ensure that there's always a single owner of the data, preventing issues like data races and dangling pointers.
6.2.2 Shallow vs. Deep Copy and the clone()
Method
If you need to retain the original value, you can create a deep copy using the clone()
method. The clone()
method creates a new instance of the data on the heap, duplicating the contents of the original data. This can be expensive depending on the size of the data, so it's important to be mindful of performance implications when using clone()
.
fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); // Creates a deep copy of s1 println!("s1: {}, s2: {}", s1, s2); }
In the code above, s1.clone()
creates a deep copy of the String
data in s1
. This new String
is then moved into s2
. The variable s1
remains valid and unchanged because the ownership of the cloned data is moved, not the original s1
. Now both s1
and s2
own separate copies of the data.
Example: Difference Between Move and Clone
fn main() { let s1 = String::from("hello"); let s2 = s1; // Move occurs // println!("{}", s1); // Error: s1 is moved let s3 = String::from("world"); let s4 = s3.clone(); // Clone occurs println!("s3: {}, s4: {}", s3, s4); // Both s3 and s4 are valid }
In this example, s1
is moved to s2
, so s1
becomes invalid. However, s3
is cloned to s4
, so both s3
and s4
remain valid.
Comparison with C
In C, you would manually copy the data:
#include <stdlib.h>
#include <string.h>
int main() {
char *s1 = malloc(6);
strcpy(s1, "hello");
char *s2 = malloc(6);
strcpy(s2, s1); // Deep copy
// Use s1 and s2
free(s1);
free(s2);
return 0;
}
6.2.3 Copying Scalar Types
For simple types like integers and floats, Rust implements copy semantics. These types implement the Copy
trait, allowing for bitwise copies without invalidating the original variable. Types that implement the Copy
trait are generally simple, stack-allocated types like integers and floats. They do not manage resources on the heap, making bitwise copies safe.
fn main() { let x = 5; let y = x; // Copy occurs println!("x: {}, y: {}", x, y); // Both x and y are valid }
Comparison with C
In C, simple types are copied by value:
int x = 5;
int y = x; // Copy