error[E0106]: missing lifetime specifier
--> src/lib.rs:1:17
|
1 | fn get_str() -> &str {
| ^ expected named lifetime parameter
A lifetime is the region of a program during which a reference is valid. Function signatures and type definitions usually have to say how the lifetimes of their references relate. Rust lets you leave many of them out, and fills them in with a few fixed rules called lifetime elision. E0106 ("missing lifetime specifier") means you left a lifetime out somewhere the elision rules cannot fill it in.
A tempting response is to add <'a> to everything until the error goes away.
That often compiles, but it can produce a signature that is stricter than it
needs to be. A better approach is to answer one question first:
Where does the data behind the returned reference come from?
The answer determines the annotation. If the answer is "from a value the function creates and then drops", no annotation can help: the function has to return an owned value instead.
The elision rules in brief
For function and method signatures, the compiler applies these rules:
- Each elided lifetime in a parameter becomes its own separate lifetime parameter.
- If there is exactly one lifetime among the inputs (elided or named), it is used for every elided lifetime in the output.
- If there are several input lifetimes but one of them belongs to
&selfor&mut self, the lifetime ofselfis used for every elided output lifetime.
If an output reference still has no lifetime after these rules, the compiler reports E0106. Struct fields, enum variants and type aliases get no elision at all, so a reference there always needs a named lifetime.
Five common forms of E0106
1. No input lifetimes at all
fn get_str() -> &str {
"hi"
}
rustc 1.98 reports:
error[E0106]: missing lifetime specifier
= help: this function's return type contains a borrowed value, but there is
no value for it to be borrowed from
No parameter is a reference, so rule 2 has nothing to copy. The compiler suggests two repairs, and these are the two real options:
- Return an owned
String. The caller receives the data and no lifetime is involved. - Return
&'static strwhen the data really lives for the whole program: a string literal, or a reference to aconstorstaticitem.fn get_str() -> &'static str { "hi" }is correct because the literal"hi"is stored in the program for its whole run.
Adding a lifetime parameter, as in fn get_str<'a>() -> &'a str, makes the
signature compile, but the caller chooses 'a, so the body must return data
that is valid for any lifetime the caller picks. In practice that means
'static data again. Returning "hi" works; returning a reference to a
String created inside the function does not compile (E0515, "cannot return
reference to local variable"). The extra parameter adds nothing over
writing 'static directly.
'static is a specific claim that the compiler checks. Use it when the data
is genuinely static, not as a way to silence the error.
2. Two reference inputs and an elided output
fn frob(s: &str, t: &str) -> &str {
s
}
error[E0106]: missing lifetime specifier
= help: this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from `s` or `t`
help: consider introducing a named lifetime parameter
|
1 | fn frob<'a>(s: &'a str, t: &'a str) -> &'a str {
There are two input lifetimes and no self, so rules 2 and 3 do not apply.
The help message states the question directly: is the result borrowed from
s or from t?
The suggested fix gives both parameters the same lifetime. That always compiles when the body returns either parameter, which makes it a reasonable default suggestion, but it is often stricter than necessary. The section "A fix that compiles but is too strict" below shows the cost.
3. A struct or enum field holding a reference
struct Excerpt { part: &str }
// ^ error[E0106]
A type that stores a reference must declare a lifetime parameter for it:
struct Excerpt<'a> { part: &'a str }
Excerpt on its own is now not a complete type. Each use names a lifetime,
as in Excerpt<'a>, or lets elision fill it in where elision is allowed. An
Excerpt<'a> value cannot outlive the string it points into.
4. A type alias
type MyStr = &str;
// ^ error[E0106]
A type alias has no parameters or receiver to take a lifetime from, so it
needs its own parameter: type MyStr<'a> = &'a str;.
5. A path to a type that has a lifetime parameter
struct View<'a> { s: &'a str }
fn make() -> View {
// ^^^^ error[E0106]
View { s: "" }
}
View has a lifetime parameter even though the name View does not show it.
Writing View in a signature is an elided lifetime, exactly like writing
&str, and here there is no input to take it from. Here the compiler suggests
View<'static>, which is correct only if every View this function returns
points at static data.
When an input does exist, the hidden form compiles. For example,
fn make(s: &str) -> View is accepted, and the output borrows from s. In
rustc 1.98 it produces the warning "hiding a lifetime that's elided elsewhere
is confusing" (lint mismatched_lifetime_syntaxes, on by default) and
suggests writing View<'_>. The '_ lifetime means "elided, apply the normal
rules", but makes it visible that View borrows something.
Consider these four signatures. Assume Config is an ordinary struct with no
lifetime parameters.
fn one(config: &Config) -> &str
fn two(name: &str, sep: char) -> &str
fn three() -> &[u8]
fn four(a: &str, b: &str) -> String
Check your understanding For each signature above, say whether E0106 occurs and, if it does, how to fix it. Show answer Hide answer
one: no error. There is one input lifetime, so by rule 2 the returned &str borrows from config. That fits a function returning, say, a name stored inside the configuration.
two: no error. char is not a reference and has no lifetime, so name provides the only input lifetime and rule 2 applies.
three: E0106, form 1. There is nothing to borrow from. If the bytes are a literal or come from a const or static, write -> &'static [u8]. If they are computed at run time, return Vec<u8>.
four: no error. The return type is owned, so it needs no lifetime. Returning an owned value removes the question entirely, which is worth considering before adding annotations.
A fix that compiles but is too strict
Back to form 2. The compiler's suggestion is:
fn frob<'a>(s: &'a str, t: &'a str) -> &'a str
It compiles. But suppose the body only ever returns a slice of s, and t is
only read. With 'a on both parameters, the signature says the result may
borrow from either input, so callers must keep both inputs alive for as long
as they use the result. This ordinary caller is then rejected:
let haystack = String::from("hello world");
let found;
{
let needle = String::from("wor");
found = frob(&haystack, &needle);
}
println!("{found}");
// error[E0597]: `needle` does not live long enough
The caller creates a temporary needle, drops it at the end of the inner
block, and keeps using found. The result really points into haystack, but
callers are checked against the signature, not the body, so the compiler
assumes it might point into needle.
Choose the signature from what the body returns:
// The result always borrows from `s`:
fn frob<'a>(s: &'a str, t: &str) -> &'a str
// The result may borrow from `s` or from `t`, depending on run-time values:
fn frob<'a>(s: &'a str, t: &'a str) -> &'a str
With the first signature the caller above compiles, because t gets its own
independent elided lifetime. If any path in the body returns a slice of t,
the first signature is rejected when the function body is checked (E0621,
"explicit lifetime required in the type of t"), and the second one is
required (or an equivalent with separate lifetimes for s and t, each
declared to outlive the result's lifetime).
Both of these functions fail with E0106 as written:
// A
fn pick(a: &str, b: &str) -> &str {
if a.len() >= b.len() { a } else { b }
}
// B
fn strip(text: &str, prefix: &str) -> &str {
text.strip_prefix(prefix).unwrap_or(text)
}
Check your understanding Which signature does each body above need, and why can't the compiler choose it for you? Show answer Hide answer
A needs fn pick<'a>(a: &'a str, b: &'a str) -> &'a str. Either branch can run, so the result may borrow from a or from b and must not outlive either of them. Tying both inputs to 'a is required here.
B needs fn strip<'a>(text: &'a str, prefix: &str) -> &'a str. Both strip_prefix(prefix) and unwrap_or(text) return slices of text; prefix is only read. Tying prefix to 'a would also compile, but it would force callers to keep their prefix alive for as long as they use the result, with no benefit.
Why doesn't the compiler infer this from the body? A function signature is the contract that callers are checked against; the compiler checks each call using only the signature. If lifetimes were inferred from the body, an edit inside the function could change its signature and break callers without any visible change to the API. Rust requires lifetimes in signatures to be stated or to follow the fixed elision rules for the same reason it requires parameter and return types to be written out.
The broader point: a signature can compile and still be stricter than necessary. The compiler checks that the body keeps the promises the signature makes. It cannot tell whether the signature makes promises you did not intend. For B, the tied form is safe but needlessly restrictive. Relaxing it later, by giving prefix its own lifetime, accepts every call that worked before, so that change is usually compatible. Going the other way, adding a constraint callers did not have before, can break existing callers.
A checklist
When you see E0106:
- Find the reference the error points at, and ask where the data behind it comes from.
- If it comes from exactly one parameter, give that parameter and the output
the same named lifetime, such as
'a, and leave the other parameters with their own elided lifetimes. - If it can come from more than one parameter, give each of those parameters and the output the same lifetime.
- If it comes from a local variable the function creates, no annotation will work. Return an owned value instead, or have the caller supply the storage (for example, by passing in a buffer or the source data as a parameter).
- If the function has no reference parameters, return an owned value, or
return
&'staticif the data really is static. - If the error is on a struct field, enum variant or type alias, add a lifetime parameter to that type or alias.
Steps 2 and 3 apply the same mechanical change to different answers from step 1. The real decision is made in step 1.