Ownership Preview
Ownership is Rust's core memory model: one owner per value, moves transfer responsibility, and borrows allow temporary access under compile-time rules. This page previews the ideas before the Ownership section.
Recipe
fn main() {
let s = String::from("hello");
let t = s; // move
// println!("{s}"); // error: moved
let len = measure(&t);
println!("{t} has length {len}");
}
fn measure(text: &String) -> usize {
text.len()
}When to reach for this: Whenever you pass data between functions, store it in structs, or share it across threads - ownership rules apply everywhere.
Working Example
fn append(mut s: String, suffix: &str) -> String {
s.push_str(suffix);
s
}
fn print_both(first: &str, second: &str) {
println!("{first} {second}");
}
fn main() {
let greeting = String::from("Hello");
let full = append(greeting, ", Rust!");
let slice: &str = &full;
print_both(slice, "welcome.");
}What this demonstrates:
appendtakes ownership ofgreetingand returns updatedString&strborrows string data without owning itStringcoerces to&strviaDeref(deref coercion)- Functions signal ownership transfer in their signatures
Deep Dive
How It Works
- Stack data with
Copyduplicates on assignment (i32,bool,char) - Heap-owning types (
String,Vec) move on assignment - When an owner goes out of scope,
dropruns automatically - References (
&T,&mut T) are borrowed views with lifetime limits
Move vs Copy
let a = 5;
let b = a; // Copy - both valid
let v1 = vec![1, 2];
let v2 = v1; // Move - v1 invalidBorrowing Rules (Summary)
- Any number of
&Tor exactly one&mut Tat a time - References must not outlive the owner
- Data races are prevented at compile time
Stack vs Heap (Preview)
let x = 42; // stack
let s = String::from("x"); // pointer + capacity + len on stack, bytes on heapGotchas
- Using a value after move - Classic beginner error with
String. Fix: Clone (s.clone()) if you need a duplicate, or borrow instead. - Taking ownership when borrowing suffices -
fn print(s: String)forces callers to give up the string. Fix: Use&stror&String. - Multiple
&mutto the same data - Compile error. Fix: Restructure scopes, useRefCellin single-threaded interior mutability, or split fields. - Returning reference to local -
fn bad() -> &Stringcannot work. Fix: Return owned value or take input by reference with lifetime tied to input. - Cloning to silence the borrow checker - Works but hides design issues. Fix: Refactor API to borrow or use
Cow.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
Borrow &T | Read-only access | Caller must keep owning for your return lifetime |
Clone | Cheap duplicate needed | Hot path with large data |
Rc/Arc | Shared ownership | Single clear owner exists |
Cow<'a, T> | Maybe borrow, maybe own | Ownership is always clear |
FAQs
Why does Rust not have a garbage collector?
Ownership and lifetimes free memory deterministically at compile time for most code, with zero runtime GC cost.
What types are `Copy`?
Scalars, tuples/arrays of Copy types, and types explicitly marked Copy. String and Vec are not Copy.
Can I opt out of moves?
Clone explicitly. There is no implicit copy for non-Copy types.
What is `Drop`?
A trait run automatically when a value goes out of scope. String frees heap memory in drop.
Does passing to a function always move?
Passing by value moves. Passing by reference &T borrows without moving ownership.
Can structs own other values?
Yes. Fields are owned like local variables. The struct is the owner of its fields.
What is a slice?
A borrowed view into contiguous data: &[T] or &str. Slices do not own memory.
Is ownership slow?
Moves are pointer copies for heap types. No reference counting unless you choose Rc/Arc.
How does ownership interact with `async`?
Owned data moves into futures. Send/Sync bound shared state across tasks (see Concurrency section).
Where is the full ownership guide?
See the Ownership section starting with Ownership Basics.
Related
- References & Slices - borrowing mechanics
- Ownership Basics - deep dive
- Move Semantics & Copy -
Copy/Clone - Borrowing & References -
&vs&mut
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.