blog.dopana

Back

Fifth post in the basic Rust series. Ownership is Rust’s most distinctive idea and the reason it is memory-safe without a garbage collector.

The rules#

  1. Each value in Rust has one owner.
  2. There can be only one owner at a time.
  3. When the owner goes out of scope, the value is dropped.
fn main() {
    let s = String::from("hello");
    println!("{s}");
}   // s goes out of scope; memory is freed
rust

Move semantics#

Assigning a value to another variable moves ownership — the old binding can no longer be used:

let s1 = String::from("hello");
let s2 = s1;
// println!("{s1}");   // error: value moved
println!("{s2}");      // ok
rust

The string is not copied; its data and ownership move to s2. This is why Rust has no use-after-free bugs.

Copy types#

Small types whose copies are cheap do get copied, not moved. These implement the Copy trait:

let a = 5;
let b = a;      // a is still usable
rust

Integers, floats, booleans, and char are Copy. String and Vec are not.

Clone#

To copy a heap-allocated value, call .clone() explicitly:

let s1 = String::from("hello");
let s2 = s1.clone();
println!("{s1} {s2}");   // both valid
rust

clone makes a deep copy. The explicit call makes the cost visible.

Ownership and functions#

Passing a value to a function moves it; returning it moves it back:

fn take_and_return(s: String) -> String {
    s
}

fn main() {
    let s = String::from("hi");
    let s = take_and_return(s);   // moved into, then back out
}
rust

Ownership and scope#

A value is dropped when its owner’s scope ends. Scope-based cleanup means resources — not just memory — are released automatically.

Why it matters#

Ownership lets the compiler prove there is no use-after-free, no double-free, and no dangling pointers — the bugs that plague C and C++. You write plain code and the compiler enforces safety.

Conclusion#

Ownership: one owner per value, moves by default, copies for Copy types, explicit clone for deep copies. Next in this series: references and slices.

References#