Course contentsShow
← Rust
Lesson 258 of 1,9103. Borrowing and ReferencesFree lesson

Combining References and Iterators

Manage the lifetime of references generated during complex iterator chains.

In this lesson, you will learn how to safely manage the lifetime of references created during iterator chains to prevent common "dangling reference" errors.

The Lifetime of an Iteration

When you chain iterators—using methods like .iter(), .map(), or .filter()—you are often creating a sequence of references that point back to an original collection. The crucial rule is that the references produced by an iterator cannot outlive the collection they are iterating over.

Consider this scenario:

fn get_first_word<'a>(data: &'a Vec<String>) -> &'a str {
    data.iter()
        .find(|s| s.starts_with("A"))
        .map(|s| s.as_str())
        .unwrap_or("")
}

Here, data.iter() generates an iterator that yields references (&String) tied to the lifetime 'a of the data vector. When we call .map(|s| s.as_str()), we are effectively transforming a reference to a String into a reference to its internal str slice. Because the slice is just a view into the original String, it inherits that same lifetime 'a.

Avoiding Lifetime Mismatches

If your iterator chain involves transformations that "capture" data, you have to be careful. If you try to return a reference that points to a temporary value created inside a map or filter closure, the Rust compiler will stop you.

For example, if you tried to create a new String inside the iterator chain and return a reference to it:

// This will fail to compile!
fn invalid_example(data: &[String]) -> &str {
    data.iter()
        .map(|s| format!("Prefix: {}", s)) // This creates a NEW String on the heap
        .next()
        .unwrap() // ERROR: Returns a reference to a local variable that dies!
}

The format! macro creates a temporary String. Once the closure finishes, that String is dropped. The iterator cannot yield a reference to something that no longer exists.

When working with iterators, always ensure your transformations are projections—views that simply point into the original, long-lived data—rather than transformations that construct new owned data. If you need to return new data, you must return the owned type instead of a reference.

Key Takeaway: Iterators create views into existing memory; ensure your iterator chain only yields references that originate from the collection itself, not from temporary calculations within the chain.