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