Ownership is Rust's unique feature — it guarantees memory safety at compile time without a garbage collector. Three simple rules:
- Each value has exactly one owner.
- When the owner goes out of scope, the value is dropped (freed).
- You can move ownership, but not copy it (for heap data).
Ownership in Action
Rust
let s1 = String::from("hello");
let s2 = s1; // ownership MOVED to s2
// println!("{s1}"); // Error: s1 is no longer valid
println!("{s2}"); // OK — s2 owns the stringWhy? If both s1 and s2 owned the same memory, Rust would
double-free it when both go out of scope. Moving prevents this.
Cloning
If you need a deep copy, use .clone():
Rust
let s1 = String::from("hello");
let s2 = s1.clone(); // deep copy — both are valid
println!("{s1}, {s2}"); // "hello, hello"References (Borrowing)
Borrow a value without taking ownership:
Rust
fn print_len(s: &String) { // & = borrow
println!("Length: {}", s.len());
}
let s = String::from("hello");
print_len(&s); // borrow
println!("{s}"); // still valid — we only borrowed it| Syntax | Meaning |
|---|---|
&T |
Immutable borrow — can read, cannot modify |
&mut T |
Mutable borrow — can read and modify |
Mutable References
Only one mutable reference at a time (prevents data races):
Rust
fn add_world(s: &mut String) {
s.push_str(", world!");
}
let mut s = String::from("hello");
add_world(&mut s);
println!("{s}"); // "hello, world!"Rules:
- You can have many immutable references (
&T) at once. - Or one mutable reference (
&mut T), but not both. - References must always be valid (no dangling pointers).
Slices
Rust
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"
let world = &s[6..11]; // "world"Tips
- Prefer references over moving ownership — use
&Tand&mut T. - Use
clone()when you genuinely need a deep copy. - The compiler will tell you exactly what to fix — trust the error messages.
- Ownership replaces garbage collection — no runtime overhead.
Next: structs & enums — building custom types.