Understand function lifetime parameters
First, note that elided lifetimes in function signatures are invisible lifetime parameters on the function.
#![allow(unused)] fn main() { fn zed(s: &str) {} }
#![allow(unused)] fn main() { // same thing fn zed<'s>(s: &'s str) {} }
When you have a lifetime parameter like this, the caller chooses the lifetime. But the body of your function is opaque to the caller: they can only choose lifetimes just longer than your function body.
So when you have a lifetime parameter on your function (without any further bounds), the only things you know are
- It's longer than your function body
- You don't get to pick it, and it could be arbitrarily long (even
'static
) - But it could be just barely longer than your function body too; you have to support both cases
And the main corollaries are
- You can't borrow locals for a caller-chosen lifetime
- You can't extend a caller-chosen lifetime to some other named lifetime in scope
- Unless there's some other outlives bound that makes it possible
Here's a couple of error examples related to function lifetime parameters:
#![allow(unused)] fn main() { fn long_borrowing_local<'a>(name: &'a str) { let local = String::new(); let borrow: &'a str = &local; } fn borrowing_zed(name: &str) -> &str { match name.len() { 0 => "Hello, stranger!", _ => &format!("Hello, {name}!"), } } }