Bird
Raised Fist0
Rustprogramming~10 mins

Lifetime annotations in Rust - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Lifetime annotations
Start: Define function with lifetimes
↓
Input references with lifetimes
↓
Check lifetime constraints
↓
Valid
↓
Return reference with correct lifetime
↓
End
The flow shows how Rust checks lifetime annotations on references to ensure safe borrowing and returns references with correct lifetimes.
Execution Sample
Rust
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("hello");
    let s2 = "world";
    let result = longest(&s1, &s2);
    println!("{}", result);
}
This code defines a function that returns the longest of two string slices with lifetime annotations ensuring safety.
Execution Table
StepActionInput lifetimesCheckReturn valueExplanation
1Call longest(&s1, &s2)'a = lifetime of s1 and s2 referencesBoth inputs have lifetime 'aN/AFunction called with two string slices having lifetime 'a
2Compare lengthsx.len()=5, y.len()=5No lifetime conflictN/ALengths compared, no lifetime issues
3Return referenceReturn &s1 or &s2Return lifetime 'a&s1Returns reference with lifetime 'a, here &s1 chosen
4Print resultresult lifetime 'aValid lifetimehelloPrints the longest string slice safely
5End mainAll lifetimes validProgram endsN/AProgram terminates successfully
💡 Execution stops after printing the longest string slice with valid lifetime annotations.
Variable Tracker
VariableStartAfter 1After 2After 3Final
s1String::from("hello")N/AN/AN/AN/A
s2"world"N/AN/AN/AN/A
x&s1 (lifetime 'a)&s1&s1&s1&s1
y&s2 (lifetime 'a)&s2&s2&s2&s2
resultN/AN/AN/A&s1&s1
Key Moments - 3 Insights
Why do we need to specify the lifetime 'a in the function signature?
Because Rust needs to know that the returned reference will live at least as long as both input references, ensuring no dangling references. See execution_table step 3 where the return lifetime 'a is enforced.
What happens if the input references have different lifetimes?
Rust will raise a compile-time error because it cannot guarantee the returned reference is valid. The function requires both inputs to share the same lifetime 'a as shown in execution_table step 1.
Why does the function return &s1 instead of &s2 when lengths are equal?
The function returns the first reference if lengths are equal due to the if condition. This choice does not affect lifetime correctness, as both have lifetime 'a (execution_table step 3).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table at step 3, what lifetime is assigned to the returned reference?
A'static
B'a
C'b
DNo lifetime
💡 Hint
Check the 'Return value' and 'Check' columns in step 3 of execution_table.
At which step does the program compare the lengths of the two string slices?
AStep 2
BStep 1
CStep 3
DStep 4
💡 Hint
Look at the 'Action' column in execution_table to find when lengths are compared.
If s2 had a shorter lifetime than s1, what would happen during the function call?
AFunction would compile and run normally
BThe function would return a reference with lifetime of s1
CRust would raise a compile-time error due to lifetime mismatch
DThe function would return a reference with lifetime of s2
💡 Hint
Refer to key_moments about lifetime mismatches and execution_table step 1.
Concept Snapshot
Lifetime annotations in Rust:
- Use syntax fn foo<'a>(x: &'a T) -> &'a T
- They tell Rust how long references live
- Ensure returned references don't outlive inputs
- Prevent dangling references and memory errors
- Lifetimes must be consistent across inputs and outputs
Full Transcript
This visual execution trace shows how Rust uses lifetime annotations to ensure safe borrowing. The function 'longest' takes two string slices with the same lifetime 'a and returns a reference with that lifetime. The execution steps show calling the function, comparing string lengths, returning a reference, and printing the result. Variables track the references and their lifetimes. Key moments clarify why lifetimes are needed and what happens if lifetimes differ. The quiz tests understanding of lifetime assignment, execution steps, and error conditions. The snapshot summarizes lifetime annotation syntax and purpose.

Practice

(1/5)
1. What is the main purpose of lifetime annotations in Rust?
easy
A. To tell Rust how long references are valid
B. To specify variable types explicitly
C. To manage memory allocation manually
D. To define function return types only

Solution

  1. Step 1: Understand lifetime annotations role

    Lifetime annotations tell Rust the scope of references to avoid dangling pointers.
  2. Step 2: Compare with other options

    Other options describe unrelated concepts like type specification or memory management.
  3. Final Answer:

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

    Lifetime annotations = reference validity [OK]
Hint: Lifetime annotations connect reference scopes [OK]
Common Mistakes:
  • Confusing lifetimes with types
  • Thinking lifetimes manage memory directly
  • Assuming lifetimes only affect return types
2. Which of the following is the correct syntax to declare a lifetime parameter named 'a in a function signature?
easy
A. fn example(input: &'a str) -> str<'a>
B. fn example<'a>(input: &'a str) -> &'a str
C. fn example<'a>(input: &str<'a>) -> &str
D. fn example(a: &str) -> &str<'a>

Solution

  1. Step 1: Identify correct lifetime syntax

    Lifetime parameters are declared with apostrophe before the name in angle brackets: 'a and used in references.
  2. Step 2: Check each option

    fn example<'a>(input: &'a str) -> &'a str correctly declares 'a and uses it for input and output references. Others misuse syntax or place lifetime incorrectly.
  3. Final Answer:

    fn example<'a>(input: &'a str) -> &'a str -> Option B
  4. Quick Check:

    Lifetime syntax = fn example<'a>(input: &'a str) [OK]
Hint: Declare lifetimes in angle brackets before parameters [OK]
Common Mistakes:
  • Placing lifetime after type instead of before
  • Omitting lifetime declaration in angle brackets
  • Using lifetime on non-reference types
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 string1 = String::from("apple");
    let string2 = "banana";
    let result = longest(string1.as_str(), string2);
    println!("{}", result);
}
medium
A. Runtime error
B. apple
C. Compilation error due to lifetimes
D. banana

Solution

  1. Step 1: Understand function longest behavior

    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 and returned.
  3. Final Answer:

    banana -> Option D
  4. Quick Check:

    Longest string = banana [OK]
Hint: Compare lengths to find longest string [OK]
Common Mistakes:
  • Assuming compilation error due to lifetimes
  • Confusing which string is longer
  • Expecting runtime error from references
4. Identify the error in this Rust code snippet involving lifetimes:
fn first_word<'a>(s: &str) -> &'a 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 parameter s
B. Cannot return a slice of s without cloning
C. Return type lifetime 'a does not match input lifetime
D. Function should return String, not &str

Solution

  1. Step 1: Check lifetime annotations on parameters and return

    The parameter s has no lifetime annotation, so Rust assigns an implicit lifetime. The return type has explicit lifetime 'a which is unrelated.
  2. Step 2: Understand lifetime mismatch

    The return lifetime 'a must be tied to the input lifetime to ensure safety. Here, 'a is unrelated, causing a mismatch error.
  3. Final Answer:

    Return type lifetime 'a does not match input lifetime -> Option C
  4. Quick Check:

    Return lifetime must match input lifetime [OK]
Hint: Return lifetime must match input reference lifetime [OK]
Common Mistakes:
  • Forgetting to add lifetime to input parameter
  • Assuming cloning is needed for slices
  • Confusing &str and String return types
5. Given two string slices with different lifetimes, how can you write a function that returns the longer slice safely using lifetime annotations? Choose the correct function signature.
hard
A. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
B. fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str
C. fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'b str
D. fn longest(x: &str, y: &str) -> &str

Solution

  1. Step 1: Understand lifetime requirements for return

    The returned reference must be valid as long as both inputs. Using a single lifetime 'a for both inputs and output ensures this.
  2. Step 2: Analyze options

    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str uses one lifetime parameter for both inputs and output, guaranteeing safety. fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str and C use different lifetimes, which can cause unsafe returns. fn longest(x: &str, y: &str) -> &str omits lifetimes, causing errors.
  3. Final Answer:

    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str -> Option A
  4. Quick Check:

    Single lifetime for inputs and output [OK]
Hint: Use one lifetime for all references to ensure safety [OK]
Common Mistakes:
  • Using different lifetimes for inputs and output
  • Omitting lifetime annotations causing errors
  • Assuming &str without lifetimes is enough
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