Rust · Intermediate
Ownership, moves and borrowing
Why it matters
Rust appeared in almost half of the Blockchain Developer job descriptions we read. Rust prevents whole classes of bugs (use after free, data races) at compile time through ownership rules. Learning to read what the compiler says about them is the first real step.
The concept
Every value has exactly one owner. When the owner goes out of scope, the value is freed.
Assigning or passing a value such as a
Stringmoves it: the old variable can no longer be used. SmallCopytypes (integers, booleans) are copied instead.To use a value without taking ownership, borrow it:
&valueis a shared, read-only reference, and&mut valueis an exclusive, mutable one.The rule: at any time you can have many shared references or one mutable reference, not both. This is what rules out data races.
.clone()makes an explicit copy when you really need two owners.
Worked example: a moved value
fn main() {
let name = String::from("asha");
let owner = name; // ownership moves to `owner`
println!("{name} {owner}");
}$ rustc --edition 2021 moved.rs
error[E0382]: borrow of moved value: `name`
--> moved.rs:4:16
|
2 | let name = String::from("asha");
| ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
3 | let owner = name; // ownership moves to `owner`
| ---- value moved here
4 | println!("{name} {owner}");
| ^^^^ value borrowed here after moveThe compiler points at all three places: where the value was created, where it moved, and where it was used afterwards. It also suggests cloning. Usually the better fix is to borrow.
Worked example: borrowing
fn total_length(names: &[String]) -> usize {
names.iter().map(|n| n.len()).sum()
}
fn add_name(names: &mut Vec<String>, name: &str) {
names.push(name.to_string());
}
fn main() {
let mut names = vec![String::from("asha"), String::from("ravi")];
println!("total before: {}", total_length(&names)); // shared borrow
add_name(&mut names, "meera"); // exclusive (mutable) borrow
println!("total after: {}", total_length(&names));
let owner = names.clone(); // an explicit copy if you need two owners
println!("{} names, clone has {}", names.len(), owner.len());
}$ rustc --edition 2021 borrow.rs && ./borrow
total before: 8
total after: 13
3 names, clone has 3total_length only reads, so it takes &[String]; add_name changes the list, so it takes &mut Vec<String>. main keeps ownership throughout.
Common mistakes
Cloning everything to silence the compiler. It works but hides design problems and costs performance.
Taking ownership in function parameters (
Vec<String>) when a borrow (&[String]) is enough.Fighting lifetimes early. Most code needs no explicit lifetime annotations; restructure first.
Interview angle
"What is ownership in Rust?" "What is the difference between &T and &mut T?" "Why can't you have a mutable and a shared reference at the same time?" (it prevents data races and iterator invalidation). "When is clone appropriate?"
Practice task
Write
longest(names: &[String]) -> Option<&String>and explain why the returned reference cannot outlive the list.Rewrite a small Python script of yours in Rust and note every compiler error you hit and what it taught you.