Bird
Raised Fist0
Rustprogramming~10 mins

Function ownership interaction in Rust - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Function ownership interaction
Define function with parameter
↓
Call function with variable
↓
Ownership moves to function
↓
Function uses variable
↓
Function ends
↓
Ownership returned or dropped
↓
Back to caller: variable usable or not?
This flow shows how ownership of a variable moves into a function when called, how the function uses it, and what happens to ownership after the function ends.
Execution Sample
Rust
fn take_ownership(s: String) {
    println!("Inside function: {}", s);
}

fn main() {
    let s = String::from("hello");
    take_ownership(s);
    // s is no longer valid here
}
This code shows a function taking ownership of a String, printing it, and then the original variable becomes invalid after the call.
Execution Table
StepActionVariable 's' OwnershipVariable 's' Usable in main?Output
1Declare s = String::from("hello")Owned by mainYes
2Call take_ownership(s)Ownership moves to functionNo
3Inside take_ownership: print sOwned by functionNoInside function: hello
4Function ends, s droppedOwnership droppedNo
5Back in main, try to use sNo ownershipNoError if used
💡 Execution stops because 's' ownership moved to function and was dropped; 's' is invalid in main after call.
Variable Tracker
VariableStartAfter CallAfter Function EndsFinal
sOwned by mainOwned by functionDroppedInvalid in main
Key Moments - 3 Insights
Why can't we use 's' in main after calling take_ownership?
Because ownership of 's' moved into the function (see step 2 in execution_table), and when the function ends, 's' is dropped, so main no longer owns it.
What happens to the variable inside the function?
Inside the function, 's' is owned by the function (step 3), so it can be used freely until the function ends.
Can we return ownership back to main to keep using 's'?
Yes, if the function returns the variable, ownership can be passed back. In this example, it is not returned, so 's' is dropped.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, at which step does 's' ownership move from main to the function?
AStep 4
BStep 3
CStep 2
DStep 5
💡 Hint
Check the 'Variable 's' Ownership' column in execution_table row for step 2.
According to variable_tracker, what is the state of 's' after the function ends?
AOwned by main
BDropped
COwned by function
DStill usable in main
💡 Hint
Look at the 'After Function Ends' and 'Final' columns for 's' in variable_tracker.
If the function returned 's' back to main, how would the 'Variable 's' Usable in main?' column change at step 5?
AIt would say 'Yes'
BIt would say 'No'
CIt would say 'Error'
DIt would be empty
💡 Hint
Think about ownership returning to main after function ends.
Concept Snapshot
fn function_name(param: Type) { ... }  
Ownership of param moves into function when called.  
Function can use or drop ownership.  
If ownership is not returned, caller loses access.  
Returning ownership lets caller keep using variable.
Full Transcript
This example shows how ownership works when passing variables to functions in Rust. When we call a function with a variable like a String, ownership moves into the function. Inside the function, the variable is fully usable. When the function ends, if ownership is not returned, the variable is dropped and the caller cannot use it anymore. This is why after calling take_ownership(s), the variable s is invalid in main. To keep using s in main, the function must return it back. This flow helps prevent bugs by making ownership clear and safe.

Practice

(1/5)
1. What happens when a function in Rust takes ownership of a variable passed to it?
easy
A. The function borrows the variable temporarily.
B. The original variable can no longer be used after the function call.
C. The variable is copied and both original and function have ownership.
D. The variable is automatically cloned inside the function.

Solution

  1. Step 1: Understand ownership transfer

    When a function takes ownership, the original variable's ownership moves to the function.
  2. Step 2: Effect on original variable

    After the function call, the original variable is no longer valid and cannot be used.
  3. Final Answer:

    The original variable can no longer be used after the function call. -> Option B
  4. Quick Check:

    Ownership moves = original invalid [OK]
Hint: Ownership moves to function, original variable becomes unusable [OK]
Common Mistakes:
  • Thinking ownership is copied, not moved
  • Confusing borrowing with ownership transfer
  • Assuming automatic cloning happens
2. Which of the following function signatures correctly borrows a string slice without taking ownership?
easy
A. fn print_name(name: &str) { println!("{}", name); }
B. fn print_name(name: &String) { println!("{}", name); }
C. fn print_name(name: String) { println!("{}", name); }
D. fn print_name(name: String) -> String { name }

Solution

  1. Step 1: Identify borrowing syntax

    Borrowing a string slice uses &str type, which is a reference to string data.
  2. Step 2: Match function signature

    fn print_name(name: &str) { println!("{}", name); } uses &str, correctly borrowing without taking ownership.
  3. Final Answer:

    fn print_name(name: &str) { println!("{}", name); } -> Option A
  4. Quick Check:

    Borrow string slice = &str [OK]
Hint: Use &str to borrow string slices, not String type [OK]
Common Mistakes:
  • Using String type takes ownership
  • Using &String borrows but is less flexible than &str
  • Returning String does not borrow
3. What is the output of the following Rust code?
fn take_ownership(s: String) {
    println!("Inside: {}", s);
}

fn main() {
    let s = String::from("hello");
    take_ownership(s);
    println!("Outside: {}", s);
}
medium
A. Inside: hello Outside: hello
B. Inside: hello Outside:
C. Inside: hello Compile error: use of moved value 's'
D. Runtime error: use after free

Solution

  1. Step 1: Analyze ownership transfer in function call

    The function take_ownership takes ownership of s, moving it inside the function.
  2. 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.
  3. Final Answer:

    Inside: hello Compile error: use of moved value 's' -> Option C
  4. Quick Check:

    Ownership moved = compile error on use after move [OK]
Hint: Using moved variable after function call causes compile error [OK]
Common Mistakes:
  • Assuming variable is still valid after move
  • Expecting runtime error instead of compile error
  • Thinking variable is copied automatically
4. Identify the error in this Rust code and choose the fix:
fn add_one(num: &i32) -> i32 {
    *num + 1
}

fn main() {
    let x = 5;
    let y = add_one(x);
    println!("{}", y);
}
medium
A. No error, code runs correctly.
B. Change function parameter to num: i32 to take ownership.
C. Change return type to &i32 to return a reference.
D. Change call to add_one(&x) to pass a reference.

Solution

  1. Step 1: Check function parameter type

    Function expects a reference &i32, but main passes x (i32) directly.
  2. Step 2: Fix function call

    Pass a reference &x to match parameter type and avoid type mismatch error.
  3. Final Answer:

    Change call to add_one(&x) to pass a reference. -> Option D
  4. Quick Check:

    Pass reference when function expects &i32 [OK]
Hint: Match argument type with parameter type exactly [OK]
Common Mistakes:
  • Passing value instead of reference
  • Changing return type unnecessarily
  • Ignoring compiler type mismatch errors
5. Given this Rust code, what is the correct way to write a function that takes ownership of a vector, adds an element, and returns ownership back?
fn add_element(vec: Vec<i32>) -> Vec<i32> {
    // Add 10 to the vector
    vec.push(10);
    vec
}
hard
A. fn add_element(mut vec: Vec<i32>) -> Vec<i32> { vec.push(10); vec }
B. fn add_element(vec: &Vec<i32>) -> Vec<i32> { vec.push(10); vec.clone() }
C. fn add_element(vec: Vec<i32>) { vec.push(10); }
D. fn add_element(vec: &mut Vec<i32>) { vec.push(10); }

Solution

  1. Step 1: Understand ownership and mutability

    To modify the vector, the function must take ownership and have mutable access, so parameter must be mutable.
  2. Step 2: Return ownership after modification

    Return the modified vector to give ownership back to the caller.
  3. Final Answer:

    fn add_element(mut vec: Vec<i32>) -> Vec<i32> { vec.push(10); vec } -> Option A
  4. Quick Check:

    Mutable ownership + return ownership = fn add_element(mut vec: Vec<i32>) -> Vec<i32> { vec.push(10); vec } [OK]
Hint: Use mut parameter and return vector to keep ownership [OK]
Common Mistakes:
  • Forgetting mut keyword for parameter
  • Trying to modify borrowed vector without mut
  • Not returning the vector after modification
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard