📖 Scenario: Imagine you are building a simple Rust program that manages references to text data. You want to ensure your program handles references safely without dangling pointers or invalid data access.
🎯 Goal: Learn how to use lifetime annotations in Rust to tell the compiler how long references should be valid. You will create a function that returns the longest string slice from two inputs, using lifetime annotations to keep the references safe.
📋 What You'll Learn
Create two string slices as input references with explicit lifetime annotations
Write a function called longest that takes two string slices with lifetime annotations and returns the longest slice
Use lifetime annotations correctly to avoid compiler errors
Print the result of calling longest with two string slices
💡 Why This Matters
🌍 Real World
Lifetime annotations are essential in Rust programs that handle references to ensure memory safety without garbage collection. They help prevent bugs like dangling pointers.
💼 Career
Understanding lifetimes is crucial for Rust developers working on systems programming, embedded software, or any application where safe and efficient memory management is required.
Progress0 / 4 steps
1
Create two string slices with explicit lifetime annotations
Create two string slices called string1 and string2 with the values "hello" and "world" respectively. Use explicit lifetime annotations &'a str in their types.
Rust
Hint
Use &str type for string slices. Lifetime annotations are usually needed in function signatures, but here just declare the slices.
2
Add a function signature with lifetime annotations
Write a function called longest that takes two string slices with the same lifetime annotation &'a str and returns a string slice with the same lifetime &'a str. Only write the function signature (header) with lifetime annotations, without the body.
Rust
Hint
Use fn longest<'a>(x: &'a str, y: &'a str) -> &'a str to declare the function with lifetime 'a.
3
Implement the function body using lifetime annotations
Implement the body of the longest function. It should compare the lengths of x and y and return the longer string slice. Use the same lifetime annotations in the function signature and body.
Rust
Hint
Use an if statement to compare lengths and return the longer slice.
4
Call the function and print the result
Call the longest function with string1 and string2 as arguments. Store the result in a variable called result and print it using println!.
Rust
Hint
Use let result = longest(string1, string2); and println!("{}", result); to display the longest string.
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
Step 1: Understand lifetime annotations role
Lifetime annotations tell Rust the scope of references to avoid dangling pointers.
Step 2: Compare with other options
Other options describe unrelated concepts like type specification or memory management.
Final Answer:
To tell Rust how long references are valid -> Option A
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
Step 1: Identify correct lifetime syntax
Lifetime parameters are declared with apostrophe before the name in angle brackets: 'a and used in references.
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.
Final Answer:
fn example<'a>(input: &'a str) -> &'a str -> Option B
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
Step 1: Understand function longest behavior
The function returns the longer string slice between x and y.
Step 2: Compare string lengths
"apple" has 5 letters, "banana" has 6 letters, so "banana" is longer and returned.
Final Answer:
banana -> Option D
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
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.
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.
Final Answer:
Return type lifetime 'a does not match input lifetime -> Option C
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.
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.
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.