title: Ownership

Ownership is Rust’s core memory management system. Instead of a garbage collector or manual malloc/free, the compiler tracks who owns each value and automatically frees memory when the owner goes out of scope.

At its core, ownership is simply about one thing: determining when a value is dropped from memory. Everything else — move semantics, borrowing, lifetimes — flows from that.

The rules

  1. Each value has exactly one owner
  2. There can only be one owner at a time
  3. When the owner goes out of scope, the value is dropped

Move semantics

Assigning a value to another variable transfers ownership — the original variable is no longer valid:

let s1 = String::from("hello");
let s2 = s1;  // ownership moves to s2
 
println!("{s1}");  // ERROR — s1 is no longer valid
println!("{s2}");  // fine

This prevents double-frees: only one variable is responsible for dropping the memory.

Copy types

Simple types that live on the stack (integers, booleans, floats, chars, tuples of those) implement Copy — they’re duplicated instead of moved:

let x = 5;
let y = x;  // x is copied, not moved
 
println!("{x}");  // fine — x is still valid
println!("{y}");  // fine

String is heap-allocated and does not implement Copy. &str (a string slice) does.

Clone

To explicitly deep-copy a heap value, use .clone():

let s1 = String::from("hello");
let s2 = s1.clone();  // s1 and s2 are independent copies
 
println!("{s1}");  // fine
println!("{s2}");  // fine

Clone can be expensive — it’s a signal that you’re duplicating heap data.

Ownership and functions

Passing a value to a function moves it, just like assignment:

fn take(s: String) {
    println!("{s}");
}  // s is dropped here
 
let s = String::from("hello");
take(s);
println!("{s}");  // ERROR — s was moved into take()

To use the value after the call, either return it back out or pass a reference instead (see [[borrowing]]).

Drop

When an owner goes out of scope, Rust calls drop() automatically — no manual memory management needed:

{
    let s = String::from("hello");
    // use s
}  // s goes out of scope here, memory is freed