</>
Skip to content
Rust lessons (20/43)

Rust — Ownership

The single most important concept in Rust

Every value has exactly one owner — a variable responsible for it. When the owner leaves scope, the value is dropped (freed). That's Rust's entire memory management story.

{
    let s = String::from("heap data");   // s owns the allocation
    println!("{}", s);
}                                        // ← s out of scope → drop() runs → memory freed

No garbage collector. No manual free. Deterministic cleanup at brace-close.

Assignment MOVES ownership

For heap data, assignment transfers ownership instead of copying:

let s1 = String::from("hello");
let s2 = s1;                 // MOVE: s1 is now invalid

println!("{}", s1);          // ❌ compile error: borrowed after move
println!("{}", s2);          // ✓ s2 is the sole owner now
Before:  s1 ──> "hello" (heap)
After:   s2 ──> "hello" (heap)     s1 = invalidated on purpose

Why? Both variables would otherwise think they own the same heap block — and both would free it at scope end (a double-free crash). Rust prevents that by invalidating s1 at compile time.

Functions take ownership too

Passing a value to a function moves it:

fn consume(s: String) {
    println!("{}", s);
}                        // s dropped here

fn main() {
    let text = String::from("mine");
    consume(text);
    println!("{}", text);   // ❌ moved into consume()
}

Getting the value back means returning it:

fn consume_and_return(s: String) -> String {
    println!("{}", s);
    s                    // hand ownership back to caller
}

Copy types are the exception

Simple stack values (integers, floats, bools, chars) implement Copy — assignment duplicates instead of moving:

let a = 5;
let b = a;        // copied, not moved
println!("{} {}", a, b);   // 5 5 ✓ both alive

Copy types: all integers/floats/bools/chars, and tuples/arrays containing only Copy members. Anything touching the heap (String, Vec) moves.

clone() — explicit deep copy

When you want BOTH variables valid:

let s1 = String::from("hello");
let s2 = s1.clone();      // real duplicate allocation

println!("{} {}", s1, s2);   // ✓ both fine — two separate strings

clone() is deliberately loud: expensive copies should be visible in code review.

The escape hatches (previews)

Ownership rules feel restrictive until you learn:

ToolMeaningLesson
References &sborrow without taking ownershipBorrowing
&mut stemporary exclusive write accessBorrowing
clone()pay for a real copythis lesson

90% of real code borrows rather than clones — borrowing is the next lesson's whole topic.

Move semantics apply to structs/vectors identically

struct Config { debug: bool }

let c1 = Config { debug: true };
let c2 = c1;                  // moved
// println!("{}", c1.debug);  ❌ same rule as String

Mini Practice

  1. Trigger "borrow of moved value"; read the full compiler note including "move occurs because…".
  2. Fix it with .clone(); then fix it by returning from the function.
  3. List which of these Copy: i32, String, bool, char, (i32, f64), Vec<i32>.
  4. Write consume(text) -> String and chain two consumes.

Next: borrowing →

Related Topics

Frequently Asked Questions about Ownership

What is Ownership in Rust?

Ownership is a fundamental concept in Rust. This lesson explains it step by step with clear examples, making it easy for beginners to understand.

How do I learn Ownership?

Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Ownership.

Why is Ownership important in Rust?

Ownership is essential for Rust development. Understanding this concept will help you write better code and solve real-world problems more effectively.