Crash Courses · Basic
Ownership, Borrowing & Lifetimes
Rust's core memory model — no GC, no dangling pointers
45 min read+160 XP on completionCert: Rust Crash Course
Tap any word in the text below to start reading from there.
Ownership, Borrowing & Lifetimes
The Three Ownership Rules
- Each value has exactly one owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value is dropped.
Move Semantics
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 is MOVED into s2 — s1 is no longer valid
// println!("{}", s1); // compile error: value moved
println!("{}", s2); // ok
}String is heap-allocated and does not implement Copy — assignment moves ownership.
Copy Types
Primitive types implement Copy — assignment duplicates the value:
let x = 5;
let y = x; // x is COPIED, not moved
println!("{} {}", x, y); // both valid: 5 5Copy types: integers, floats, bool, char, tuples of Copy types.
Cloning
Explicitly deep-copy a non-Copy type:
let s1 = String::from("hello");
let s2 = s1.clone(); // explicit heap allocation
println!("{} {}", s1, s2); // both validBorrowing (References)
Pass a reference to avoid moving ownership:
fn length(s: &String) -> usize { // borrows s
s.len()
}
fn main() {
let s = String::from("hello");
let len = length(&s); // pass reference
println!("{} has {} chars", s, len); // s still valid
}Mutable References
fn append_world(s: &mut String) {
s.push_str(", world");
}
fn main() {
let mut s = String::from("hello");
append_world(&mut s);
println!("{}", s); // hello, world
}Rule: you can have EITHER any number of &T OR exactly one &mut T — never both simultaneously. This prevents data races at compile time.
Lifetimes
Lifetimes annotate how long references live. The compiler infers most of them:
// 'a says: the returned reference lives as long as the shorter-lived input
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let s1 = String::from("long string");
let result;
{
let s2 = String::from("xy");
result = longest(s1.as_str(), s2.as_str());
println!("{}", result); // ok — both alive here
}
}You rarely write lifetime annotations in practice — the compiler's lifetime elision rules handle most cases.
Reading progress
0% read
In this module
Ownership
Move
Copy
Borrow (&T)
0%