Move Semantics Exercises
Overview
These exercises cover Rust’s unique ownership model — the heart of memory safety without garbage collection.
move_semantics1 - Ownership & Mutability Inside Functions
Concept: Making a moved value mutable
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec; // Shadow with mutable binding
vec.push(88);
vec
}Key Takeaways:
- When you pass a value to a function, ownership moves
- The original variable can no longer be used
- You can shadow the parameter to make it mutable inside the function
move_semantics2 - Clone to Keep Original
Concept: Using .clone() to avoid moving
#[test]
fn move_semantics2() {
let vec0 = vec![22, 44, 66];
// Clone creates a deep copy — vec0 stays valid
let vec1 = fill_vec(vec0.clone());
assert_eq!(vec0, [22, 44, 66]); // vec0 still works!
assert_eq!(vec1, [22, 44, 66, 88]);
}Key Takeaway: .clone() creates a copy, so you don’t lose access to the original.
⚠️ Trade-off: Cloning has a performance cost — it copies all data.
move_semantics3 - Mutable Parameters
Concept: Declaring parameters as mutable
fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
// ^^^ mut in parameter position
vec.push(88);
vec
}Key Takeaway: You can put mut directly in the parameter to avoid shadowing.
move_semantics4 - Mutable References
Concept: Multiple mutable borrows (non-overlapping)
#[test]
fn move_semantics4() {
let mut x = Vec::new();
let y = &mut x;
y.push(42);
// y is no longer used after this point
let z = &mut x; // OK! y's borrow has ended
z.push(13);
assert_eq!(x, [42, 13]);
}Key Takeaways:
- Only one mutable reference at a time
- But borrows end when no longer used (Non-Lexical Lifetimes)
- This prevents data races at compile time!
move_semantics5 - Borrow vs Move
Concept: Choosing between borrowing and taking ownership
// Borrows — doesn't consume the String
fn get_char(data: &String) -> char {
data.chars().last().unwrap()
}
// Takes ownership — consumes the String
fn string_uppercase(mut data: String) {
data = data.to_uppercase();
println!("{data}");
}
fn main() {
let data = "Rust is great!".to_string();
get_char(&data); // Borrow with &
string_uppercase(data); // Move ownership
}Decision Guide:
| If you need to… | Use |
|---|---|
| Read without changing | &T (immutable borrow) |
| Modify temporarily | &mut T (mutable borrow) |
| Consume/transform completely | T (take ownership) |
The Ownership Rules
- Each value in Rust has exactly one owner
- When the owner goes out of scope, the value is dropped
- You can have either:
- One mutable reference (
&mut T) - Any number of immutable references (
&T) - But not both at the same time!
- One mutable reference (