Rust Ownership and Borrowing Explained Simply
Ownership is the idea that makes Rust memory-safe without a garbage collector. It comes down to three rules and two kinds of references.
The three ownership rules
- Each value has exactly one owner (a variable).
- When the owner goes out of scope, the value is dropped (memory freed).
- Ownership can be moved to another variable or function.
Moves
let a = String::from("hello");
let b = a; // ownership moves to b
println!("{a}"); // error: value borrowed here after move
Simple types like integers and bool implement Copy, so they're copied instead of moved. To duplicate a String, call .clone() explicitly.
Borrowing with &
Usually you want to let a function use a value without taking it. Pass a reference:
fn word_count(text: &str) -> usize {
text.split_whitespace().count()
}
let article = String::from("Rust is fast and safe");
let n = word_count(&article); // borrow
println!("{article} has {n} words"); // still usable
Mutable borrowing with &mut
fn add_tag(tags: &mut Vec<String>, tag: &str) {
tags.push(tag.to_string());
}
let mut tags = vec![];
add_tag(&mut tags, "rust");
The borrowing rule
At any moment you can have either any number of shared references (&T) or exactly one mutable reference (&mut T), never both.
let mut scores = vec![10, 20];
let first = &scores[0]; // shared borrow
scores.push(30); // error: cannot borrow as mutable while also borrowed as immutable
println!("{first}");
This is a real bug in other languages: push may reallocate the vector, leaving first pointing at freed memory. Rust stops it at compile time.
Practical tips
- Take
&strand&[T]in function parameters; they accept more types than&Stringand&Vec<T>. - Return owned values (
String,Vec<T>) from functions that create data. - Cloning is fine when it keeps code simple; optimise later if profiling says so.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.