Start learning free

LESSON 15 OF 414 · VARIABLES

Ownership and Moves in Rust: Move, Clone and Copy

Lesson Read the full explanation

Every value in Rust has exactly one owner — the variable that is responsible for it. When the owner goes out of scope, the value is cleaned up automatically. This is how Rust manages memory without a garbage collector, and it is the single most important idea in the language.

The surprising part: assigning a String to another variable moves it. The new variable becomes the owner, and the old variable is dead — you are not allowed to touch it anymore.


Moving a value:

let s1 = String::from("AlgoCademy");
let s2 = s1;             // the String MOVES from s1 into s2
println!("s2 = {}", s2); // OK — s2 owns it now

After let s2 = s1; there is still only one copy of the text in memory — it just has a new owner. Nothing was duplicated, which makes moves very cheap.


The trap — using a variable after it moved:

If you try to use s1 after the move, the program does not even compile:

let s1 = String::from("AlgoCademy");
let s2 = s1;
println!("s1 = {}", s1); // ← using the OLD owner

The compiler stops you with this error:

error[E0382]: borrow of moved value: `s1`
 --> main.rs:4:25
  |
2 |     let s1 = String::from("AlgoCademy");
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1;
  |              -- value moved here
4 |     println!("s1 = {}", s1);
  |                         ^^ value borrowed here after move
  |
help: consider cloning the value if the performance cost is acceptable
  |
3 |     let s2 = s1.clone();
  |                ++++++++

Read it bottom-up: s1 was moved on line 3, so line 4 may not use it. The compiler even suggests the fix: if you really need two copies, call .clone() — it duplicates the data for real, and both variables stay usable.


Copy types — the exception:

Simple fixed-size types like i32, f64, bool and char implement the Copy trait. Assigning them just copies the bits, so the original stays valid:

let a = 7;
let b = a;                       // copied, not moved
println!("{} {}", a, b);         // both fine — prints: 7 7

Assignment
Follow the Coding Tutorial and prove all three rules to yourself:

  1. Create s1 as String::from("AlgoCademy") and move it into s2, then print s2.
  2. Clone s2 into s3 and print both — cloning keeps the original alive.
  3. Create n1 = 7, assign it to n2, and print both — i32 is Copy, so nothing moves.

The expected output is:

s2 = AlgoCademy
s2 = AlgoCademy, s3 = AlgoCademy
n1 = 7, n2 = 7

Hint
If the compiler says borrow of moved value, ask yourself: who owns this value right now?


STEP 1 OF 8
    Ctrl + Enter to check

    RUST · VARIABLES

    Ownership And Moves in Rust, explained

    One value, one owner

    Ownership is the central idea of Rust, and it replaces both manual memory management and a garbage collector. The rules are short:

    • Every value has exactly one owner, the variable responsible for it.
    • When the owner goes out of scope, the value is cleaned up automatically.
    • Assigning a value that owns memory to another variable moves it. The new variable becomes the owner, and the old one can no longer be used.

    The compiler checks these rules before the program runs. If the code breaks one, it doesn't compile.

    Cleanup happens at the closing brace

    The cleanup in the second rule is exact. When a variable goes out of scope, the value it owns is dropped at that point. A small type that prints a message when dropped makes this visible. Declare one value in an inner block and another in the outer function, and the inner one is dropped at the closing brace of its block, while the outer one is dropped when the function ends. Because there is only ever one owner, a value is cleaned up exactly once, never twice and never forgotten.

    Moving a String

    let s1 = String::from("AlgoCademy");
    let s2 = s1;
    println!("s2 = {}", s2);
    

    After let s2 = s1; the text exists once in memory, and s2 owns it. Nothing was duplicated, so a move is cheap: only the small handle that points at the text is transferred. The print shows s2 = AlgoCademy.

    Using a variable after it moved

    Now try to use the old owner:

    let s1 = String::from("AlgoCademy");
    let s2 = s1;
    println!("s1 = {}", s1);
    

    The program is rejected with error[E0382]: borrow of moved value: `s1`. Read the message from the notes up. The first one says move occurs because `s1` has type `String`, which does not implement the `Copy` trait. The second marks value moved here at let s2 = s1;. The last marks value borrowed here after move at the print. The help line suggests s1.clone().

    Moves also happen when you pass a String to a function by value. After show(s) with fn show(s: String), the variable s in the caller is moved and the same error appears if it is used again. A Vec moves in the same way. Passing a reference instead, show(&s) with fn show(s: &String), only lends the value, and the caller keeps ownership.

    Clone: a real copy

    If you need two independent values, ask for a duplicate:

    let s1 = String::from("AlgoCademy");
    let s2 = s1;
    let s3 = s2.clone();
    println!("s2 = {}, s3 = {}", s2, s3);
    

    clone() allocates new memory and copies the text, so s2 and s3 each own their own data and both stay usable. It costs time and memory proportional to the size of the data, which is why Rust makes you write it out instead of doing it silently.

    Copy types are the exception

    Small values of fixed size, such as i32, f64, bool and char, implement the Copy trait. Assigning one just copies its bits, and the original stays valid:

    let n1 = 7;
    let n2 = n1;
    println!("n1 = {}, n2 = {}", n1, n2);
    

    This prints n1 = 7, n2 = 7. A string literal such as "hi" is a &str, a shared reference, which is Copy too, and a tuple of Copy values is Copy. The rule of thumb: types that own heap memory, like String and Vec, move; simple values copy.

    Solving the exercise

    The exercise walks through the three behaviors in turn, printing after each one:

    1. Create the owner. let s1 = String::from("AlgoCademy"); makes an owned String, which is the kind of value that moves.
    2. Move it. let s2 = s1; is a plain assignment with no clone. From here, s1 is off limits.
    3. Print the new owner. println!("s2 = {}", s2);. Printing s1 instead would not compile.
    4. Clone. let s3 = s2.clone(); duplicates the data, and s2 stays alive.
    5. Print both in one println!, with two {} placeholders filled in order.
    6. Create and copy an integer. let n1 = 7; and let n2 = n1; need no clone, because i32 is Copy.
    7. Print both numbers, which works because n1 was never invalidated.
    let s1 = String::from("AlgoCademy");
    let s2 = s1;
    println!("s2 = {}", s2);
    let s3 = s2.clone();
    println!("s2 = {}, s3 = {}", s2, s3);
    let n1 = 7;
    let n2 = n1;
    println!("n1 = {}, n2 = {}", n1, n2);
    

    The output is:

    s2 = AlgoCademy
    s2 = AlgoCademy, s3 = AlgoCademy
    n1 = 7, n2 = 7
    

    Common mistakes

    • Using the old variable after a move. Ask who owns the value right now. If the answer isn't the variable you just wrote, that is the error.
    • Reaching for clone() every time. It silences the error, but it also copies the data. Often a reference (&) is the better fix, because it lends the value without copying.
    • Expecting every type to move. Integers, floats, booleans and chars copy, so the same assignment that breaks for a String works for them.

    Key takeaways

    • Each value has one owner, and it is cleaned up when the owner goes out of scope.
    • Assigning a String (or passing it to a function by value) moves it, and the old variable becomes unusable.
    • clone() makes a real duplicate, so both variables stay valid.
    • Copy types like i32 are copied on assignment and never move.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free