Functions in Rust can take ownership of values, borrow them, or return ownership. Understanding this helps you manage memory safely without errors.
Function ownership interaction in Rust
fn function_name(param: Type) -> ReturnType {
// function body
}Parameters can be passed by value (taking ownership) or by reference (borrowing).
Returning a value transfers ownership back to the caller.
fn take_ownership(s: String) { println!("{}", s); } // s is dropped here
fn borrow_string(s: &String) { println!("{}", s); } // s is borrowed, not owned
fn give_ownership() -> String { let s = String::from("hello"); s // ownership moved to caller }
This program shows three ways functions interact with ownership: taking ownership, borrowing, and returning ownership.
fn take_ownership(s: String) { println!("Inside function: {}", s); } fn borrow_string(s: &String) { println!("Inside function: {}", s); } fn give_ownership() -> String { let s = String::from("hello from function"); s } fn main() { let s1 = String::from("hello"); take_ownership(s1); // s1 can no longer be used here let s2 = String::from("world"); borrow_string(&s2); println!("After borrow: {}", s2); // s2 still valid let s3 = give_ownership(); println!("Received ownership: {}", s3); }
When a function takes ownership, the original variable can no longer be used after the call.
Borrowing with references allows safe temporary access without losing ownership.
Returning ownership lets you create and send data back to the caller.
Functions can take ownership, borrow, or return ownership of values.
Ownership rules help prevent bugs and memory errors.
Use borrowing to let functions read data without taking ownership.
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
