Rust's ownership system is its most distinctive feature, enabling memory safety without garbage collection.
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 is moved to s2
// println!("{}", s1); // Error! s1 is no longer valid
println!("{}", s2); // OK
}
When s1 is assigned to s2, ownership moves. This prevents double-free errors.
Instead of moving, we can borrow:
fn calculate_length(s: &String) -> usize {
s.len()
} // s goes out of scope but doesn't own the data
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("{} has length {}", s1, len); // s1 still valid!
}
The & creates a reference that borrows the value without taking ownership.
For mutations, use &mut:
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"
}
&T)&mut T)This prevents data races at compile time!
let data = vec![1, 2, 3];
let r1 = &data;
let r2 = &data;
println!("{:?} {:?}", r1, r2); // OK
let mut data = vec![1, 2, 3];
let r = &mut data;
r.push(4);
// Can't create another reference while r exists
Ownership and borrowing eliminate entire classes of bugs:
All caught at compile time with zero runtime overhead.
The ownership system might seem restrictive at first, but it's Rust's superpower—enabling fearless concurrency and memory safety without garbage collection.