Bird
Raised Fist0
Rustprogramming~10 mins

Lifetimes in functions in Rust - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Lifetimes in functions
Function called with references
↓
Check lifetime annotations
↓
Compare input lifetimes
↓
Valid
↓
Return reference with correct lifetime
↓
Function ends
This flow shows how Rust checks lifetime annotations in functions to ensure returned references are valid and safe.
Execution Sample
Rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

let s1 = String::from("hello");
let s2 = "world!";
let result = longest(&s1, &s2);
This function returns the longest of two string slices, using lifetime annotations to ensure safety.
Execution Table
StepActionInput lifetimesComparisonReturn valueNotes
1Function longest called&'a str, &'a strCheck lifetimes 'a and 'aN/ABoth inputs have lifetime 'a
2Compare lengthss1.len()=5, s2.len()=65 > 6Return s2s2 is longer
3Return referenceReturn &'a strN/A&s2Return reference tied to lifetime 'a
4Assign to resultresult: &'a strN/A"world!"result holds reference to s2
5Function endsN/AN/AN/ALifetimes ensure no dangling refs
💡 Function returns reference with lifetime 'a, ensuring safety of returned reference
Variable Tracker
VariableStartAfter Step 2After Step 3Final
x&s1 ("hello")&s1 ("hello")&s1 ("hello")&s1 ("hello")
y&s2 ("world!")&s2 ("world!")&s2 ("world!")&s2 ("world!")
resultuninitializeduninitialized&s2 ("world!")&s2 ("world!")
Key Moments - 3 Insights
Why do we need to specify lifetime 'a in the function signature?
Because Rust needs to know that the returned reference lives at least as long as both input references. This is shown in the execution_table step 1 where lifetimes are checked.
What happens if the input references have different lifetimes?
Rust will require the returned reference to have the shorter lifetime of the inputs to avoid dangling references. This is implied in the comparison step in the execution_table.
Why can't we return a reference without lifetime annotations?
Without lifetime annotations, Rust cannot guarantee the returned reference is valid, so it will cause a compile error. This safety check is part of the flow in the concept_flow diagram.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table at step 2, which string slice is longer?
As1 ("hello")
Bs2 ("world!")
CBoth are equal
DCannot determine
💡 Hint
Check the 'Comparison' column in step 2 of the execution_table
At which step is the returned reference assigned to the variable 'result'?
AStep 1
BStep 2
CStep 4
DStep 3
💡 Hint
Look at the 'Action' column in the execution_table for assignment details
If the input lifetimes were different, how would Rust determine the lifetime of the returned reference?
AIt uses the shorter lifetime
BIt uses the longer lifetime
CIt ignores lifetimes
DIt returns an owned String instead
💡 Hint
Refer to the key_moments explanation about lifetime comparisons
Concept Snapshot
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  // returns longest string slice
}

- Lifetime 'a ties inputs and output
- Ensures returned ref lives long enough
- Prevents dangling references
- Compiler checks lifetimes at compile time
Full Transcript
This visual trace shows how Rust functions use lifetimes to ensure references returned are valid. The function longest takes two string slices with the same lifetime 'a and returns the longer one with that lifetime. Step by step, the function compares lengths, returns the correct reference, and assigns it to a variable. Lifetimes prevent unsafe references by tying the output lifetime to the inputs. If lifetimes differ, Rust uses the shorter one to keep safety. This prevents bugs and crashes from invalid memory access.

Practice

(1/5)
1. What is the main purpose of lifetimes in Rust functions?
easy
A. To control thread execution order
B. To tell Rust how long references are valid
C. To define variable types explicitly
D. To manage memory allocation size

Solution

  1. Step 1: Understand lifetimes concept

    Lifetimes specify how long references remain valid to prevent dangling pointers.
  2. Step 2: Identify purpose in functions

    Functions use lifetimes to connect input and output references safely.
  3. Final Answer:

    To tell Rust how long references are valid -> Option B
  4. Quick Check:

    Lifetimes = reference validity [OK]
Hint: Lifetimes track reference validity duration [OK]
Common Mistakes:
  • Confusing lifetimes with memory size
  • Thinking lifetimes manage variable types
  • Assuming lifetimes control threads
2. Which of the following is the correct syntax to declare a function with a lifetime parameter 'a that returns a reference with the same lifetime?
easy
A. fn example<'a>(x: &'a str) -> &'a str { x }
B. fn example(x: &str) -> &str { x }
C. fn example<'a>(x: &str) -> &'a str { x }
D. fn example(x: &'a str) -> &str { x }

Solution

  1. Step 1: Identify lifetime parameter usage

    The lifetime 'a must be declared and used consistently in parameters and return type.
  2. Step 2: Check each option's correctness

    fn example<'a>(x: &'a str) -> &'a str { x } correctly declares 'a and uses it for input and output references.
  3. Final Answer:

    fn example<'a>(x: &'a str) -> &'a str { x } -> Option A
  4. Quick Check:

    Lifetime declared and used consistently [OK]
Hint: Declare and use lifetime parameter consistently [OK]
Common Mistakes:
  • Omitting lifetime declaration
  • Using lifetime only in return type
  • Using lifetime only in parameter
3. What will be the output of this Rust code?
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let s1 = String::from("apple");
    let s2 = String::from("banana");
    let result = longest(&s1, &s2);
    println!("{}", result);
}
medium
A. Runtime error
B. apple
C. Compilation error due to lifetimes
D. banana

Solution

  1. Step 1: Understand function logic

    The function returns the longer string slice between x and y.
  2. Step 2: Compare string lengths

    "apple" has 5 letters, "banana" has 6 letters, so "banana" is longer.
  3. Final Answer:

    banana -> Option D
  4. Quick Check:

    Longest string = banana [OK]
Hint: Compare lengths, return longer string slice [OK]
Common Mistakes:
  • Assuming compilation error due to lifetimes
  • Confusing which string is longer
  • Expecting runtime error
4. Identify the error in this Rust function and how to fix it:
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s
}
medium
A. Missing lifetime annotation on function signature
B. Incorrect byte comparison syntax
C. Return type should be String, not &str
D. Function should take String, not &str

Solution

  1. Step 1: Analyze function signature

    The function returns a reference to a slice of the input string, so lifetimes must be connected.
  2. Step 2: Identify missing lifetime annotation

    Without lifetime 'a, Rust cannot ensure the returned reference is valid as long as input.
  3. Final Answer:

    Missing lifetime annotation on function signature -> Option A
  4. Quick Check:

    Lifetime needed for input and output references [OK]
Hint: Add lifetime to input and output references [OK]
Common Mistakes:
  • Ignoring lifetime errors
  • Changing return type unnecessarily
  • Confusing byte comparison syntax
5. Given two string slices with different lifetimes, how can you write a function that returns the longer slice without lifetime errors?
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() { x } else { y }
}
hard
A. Use different lifetime parameters for inputs and output
B. Return a String instead of a reference to avoid lifetimes
C. Add a single lifetime parameter 'a to both inputs and output: fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
D. Remove references and pass owned Strings

Solution

  1. Step 1: Understand lifetime mismatch

    Without lifetime annotations, Rust cannot guarantee output reference lives as long as inputs.
  2. Step 2: Use a single lifetime parameter

    Declaring 'a for both inputs and output ties their lifetimes together, resolving errors.
  3. Final Answer:

    Add a single lifetime parameter 'a to both inputs and output: fn longest<'a>(x: &'a str, y: &'a str) -> &'a str -> Option C
  4. Quick Check:

    Single lifetime parameter ties input/output lifetimes [OK]
Hint: Use one lifetime parameter for all references [OK]
Common Mistakes:
  • Using different lifetimes for inputs and output
  • Returning owned String unnecessarily
  • Removing references instead of fixing lifetimes
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