Skip to content

Rust Error E0106: Missing Lifetime Specifier

Error E0106 means: you wrote a reference (&) in a function’s return type or a struct, and Rust can’t tell what it borrows from. It’s not a bug in your logic. The compiler just needs one more word from you, or a small change of plan. Almost everyone meets this error in their first week, and the fix is usually a single line. (This page is part of Rust compiler errors explained.)

error[E0106]: missing lifetime specifier
--> src/main.rs:1:32
|
1 | fn longer(a: &str, b: &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
help: consider introducing a named lifetime parameter
|
1 | fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
| ++++ ++ ++ ++

The program that causes it:

fn longer(a: &str, b: &str) -> &str {
if a.len() >= b.len() { a } else { b }
}
fn main() {
println!("{}", longer("apple", "fig"));
}

A reference is a borrow: it points at data that somebody else owns. Whenever a function returns a reference, Rust needs to know whose data it points into, so it can check that the data stays alive while the caller uses the result.

Look at the three marks under line 1. The compiler underlines a and b with ---- and the return type with ^. It is saying: “you return a borrowed &str, and it could come from a or from b. Which one?” It won’t guess.

Think of a library card. If a friend hands you a book and says “it’s borrowed”, you want to know from whom, so you know when it must go back. A lifetime is that label on the borrow.

Rust guarantees that a reference can never outlive the data it points to (no “dangling pointers”). To check that for a function call, the compiler only looks at the function’s signature, not its body. So the signature itself must say where the returned reference comes from.

Most of the time you never write lifetimes, because Rust fills them in with three simple elision rules:

  1. Each reference parameter gets its own lifetime.
  2. If there is exactly one reference parameter, the returned reference borrows from it.
  3. If it’s a method with &self or &mut self, the returned reference borrows from self.

E0106 appears when none of these rules gives an answer: two or more reference parameters (and no self), zero reference parameters, or a reference stored in a struct. The Lifetimes lesson covers this in depth, and Borrowing explains references themselves.

Choose this when the function really does return a piece of one of its inputs. 'a is just a name. Writing it on the inputs and the output says “the result lives as long as these inputs”:

// 'a: the returned &str borrows from a or b, so it lives as long as both.
fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() { a } else { b }
}
fn main() {
let first = String::from("apple");
let second = String::from("fig");
println!("Longer word: {}", longer(&first, &second));
}

Output:

Longer word: apple
▶ Try it Yourself

If the result only ever comes from one parameter, put 'a on that parameter only: fn lookup<'a>(map: &'a HashMap<String, String>, key: &str) -> Option<&'a str>. The compiler’s suggestion puts 'a on everything, which works but is stricter than needed.

Choose this when the function builds new text, or when you just don’t want the caller tied to a borrow. An owned String belongs to the caller, so there is nothing to track:

fn greeting(name: &str, lang: &str) -> String {
if lang == "es" {
format!("Hola, {name}!")
} else {
format!("Hello, {name}!")
}
}
fn main() {
println!("{}", greeting("Ana", "es"));
println!("{}", greeting("Sam", "en"));
}

Output:

Hola, Ana!
Hello, Sam!
▶ Try it Yourself

This costs one small allocation. For most programs that’s completely fine. Don’t feel bad about it.

A struct field like title: &str gives the same error:

error[E0106]: missing lifetime specifier
--> src/main.rs:2:12
|
2 | title: &str,
| ^ expected named lifetime parameter

For structs, the best fix is nearly always to own the data: use String instead of &str, and Vec<T> instead of &[T]. Then the struct can live as long as you like:

#[derive(Debug)]
struct Book {
title: String, // owned, not &str
pages: u32,
}
fn main() {
let book = Book { title: String::from("Dune"), pages: 412 };
println!("{} ({} pages)", book.title, book.pages);
}

Output:

Dune (412 pages)
▶ Try it Yourself

Only give a struct a lifetime when it is a short-lived view into data that lives somewhere else, like a parser looking at a big text. Then you write <'a> on the struct and on the field:

struct Excerpt<'a> {
text: &'a str, // borrows from the novel below
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first = novel.split('.').next().unwrap();
let excerpt = Excerpt { text: first };
println!("Excerpt: {}", excerpt.text);
}

Output:

Excerpt: Call me Ishmael
▶ Try it Yourself

When a function has no reference parameters at all, the error says “there is no value for it to be borrowed from”:

error[E0106]: missing lifetime specifier
--> src/main.rs:1:22
|
1 | fn default_name() -> &str {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from

If you only return string literals like "OK", write &'static str. Literals are baked into your program, so they live for the whole run:

fn status_text(code: u16) -> &'static str {
match code {
200 => "OK",
404 => "Not Found",
_ => "Unknown",
}
}
fn main() {
println!("200 means {}", status_text(200));
println!("404 means {}", status_text(404));
}

Output:

200 means OK
404 means Not Found
▶ Try it Yourself

Use 'static only for real literals and constants. If the text is built at runtime (with format!, or read from a file), 'static won’t help: return a String (Fix 2).

  • Helper functions with two inputs, like fn lookup(map: &HashMap<String, String>, key: &str) -> Option<&str>. Fix: tie the output to map only with 'a (Fix 1).
  • Structs that hold text, like a User { name: &str } built from parsed input. Fix: use String fields (Fix 3).
  • Functions returning fixed messages, like fn error_label() -> &str. Fix: &'static str (Fix 4).

Methods are the good news: fn name(&self) -> &str needs no lifetime at all, thanks to rule 3.

Quick check

1. Why does fn first_word(s: &str) -> &str compile without any lifetime?

2. Your struct Config { path: &str } gives E0106. What's usually the best fix?

3. A function builds a message with format! and returns it. What return type should it have?

Score: 0 / 3