Function ownership interaction in Rust - Time & Space Complexity
When functions take ownership of data in Rust, it affects how many times data is moved or copied.
We want to see how this ownership passing impacts the number of operations as input grows.
Analyze the time complexity of the following Rust code snippet.
fn consume_vector(v: Vec) {
for item in v {
println!("{}", item);
}
}
fn main() {
let data = vec![1, 2, 3, 4, 5];
consume_vector(data);
}
This code moves ownership of a vector into a function and prints each item.
Look for loops or repeated actions that take time.
- Primary operation: Looping over each item in the vector inside
consume_vector. - How many times: Once for each element in the vector (n times).
As the vector gets bigger, the function prints more items, so work grows with the number of elements.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 print operations |
| 100 | 100 print operations |
| 1000 | 1000 print operations |
Pattern observation: The work grows directly with the number of items; doubling items doubles work.
Time Complexity: O(n)
This means the time to run grows in a straight line with the number of elements.
[X] Wrong: "Passing ownership to a function copies the data, so it takes extra time beyond the loop."
[OK] Correct: In Rust, ownership transfer is a move, not a copy, so it does not add extra repeated work proportional to input size.
Understanding how ownership affects function calls helps you write efficient Rust code and explain your reasoning clearly.
What if the function borrowed the vector instead of taking ownership? How would the time complexity change?
Practice
Solution
Step 1: Understand ownership transfer
When a function takes ownership, the original variable's ownership moves to the function.Step 2: Effect on original variable
After the function call, the original variable is no longer valid and cannot be used.Final Answer:
The original variable can no longer be used after the function call. -> Option BQuick Check:
Ownership moves = original invalid [OK]
- Thinking ownership is copied, not moved
- Confusing borrowing with ownership transfer
- Assuming automatic cloning happens
Solution
Step 1: Identify borrowing syntax
Borrowing a string slice uses &str type, which is a reference to string data.Step 2: Match function signature
fn print_name(name: &str) { println!("{}", name); } uses &str, correctly borrowing without taking ownership.Final Answer:
fn print_name(name: &str) { println!("{}", name); } -> Option AQuick Check:
Borrow string slice = &str [OK]
- Using String type takes ownership
- Using &String borrows but is less flexible than &str
- Returning String does not borrow
fn take_ownership(s: String) {
println!("Inside: {}", s);
}
fn main() {
let s = String::from("hello");
take_ownership(s);
println!("Outside: {}", s);
}Solution
Step 1: Analyze ownership transfer in function call
The function take_ownership takes ownership of s, moving it inside the function.Step 2: Check usage after ownership moved
After the function call, s is no longer valid in main, so using it causes a compile error.Final Answer:
Inside: hello Compile error: use of moved value 's' -> Option CQuick Check:
Ownership moved = compile error on use after move [OK]
- Assuming variable is still valid after move
- Expecting runtime error instead of compile error
- Thinking variable is copied automatically
fn add_one(num: &i32) -> i32 {
*num + 1
}
fn main() {
let x = 5;
let y = add_one(x);
println!("{}", y);
}Solution
Step 1: Check function parameter type
Function expects a reference &i32, but main passes x (i32) directly.Step 2: Fix function call
Pass a reference &x to match parameter type and avoid type mismatch error.Final Answer:
Change call to add_one(&x) to pass a reference. -> Option DQuick Check:
Pass reference when function expects &i32 [OK]
- Passing value instead of reference
- Changing return type unnecessarily
- Ignoring compiler type mismatch errors
fn add_element(vec: Vec<i32>) -> Vec<i32> {
// Add 10 to the vector
vec.push(10);
vec
}Solution
Step 1: Understand ownership and mutability
To modify the vector, the function must take ownership and have mutable access, so parameter must be mutable.Step 2: Return ownership after modification
Return the modified vector to give ownership back to the caller.Final Answer:
fn add_element(mut vec: Vec<i32>) -> Vec<i32> { vec.push(10); vec } -> Option AQuick Check:
Mutable ownership + return ownership = fn add_element(mut vec: Vec<i32>) -> Vec<i32> { vec.push(10); vec } [OK]
- Forgetting mut keyword for parameter
- Trying to modify borrowed vector without mut
- Not returning the vector after modification
Ready to prove it?
Pass the test and this pattern moves unknown
