rust-lang / rust-lang/rust

Lifetime error sometimes shows a misleading root cause

Open
#144,040 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-diagnostics T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
fn good1<'a>(value: &'a str) -> &'static str {
    value
}

fn good2<'a>(value: &'a str) {
    core::convert::identity::<&'static str>(value);
}

fn bad1<'a>(value: &'a str) {
    let _p: &'static str = value;
}

use core::marker::PhantomData;
fn bad2<'a>(value: &'a str) {
    let phantom = PhantomData::<*mut &'static str>;
    unify(phantom, value);
}
fn unify<T>(_phantom: PhantomData<*mut T>, _value: T) {}
Current output
error: lifetime may not live long enough
 --> src/lib.rs:2:5
  |
1 | fn good1<'a>(value: &'a str) -> &'static str {
  |          -- lifetime `'a` defined here
2 |     value
  |     ^^^^^ returning this value requires that `'a` must outlive `'static`

error[E0521]: borrowed data escapes outside of function
 --> src/lib.rs:6:5
  |
5 | fn good2<'a>(value: &'a str) {
  |          --  ----- `value` is a reference that is only valid in the function body
  |          |
  |          lifetime `'a` defined here
6 |     core::convert::identity::<&'static str>(value);
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |     |
  |     `value` escapes the function body here
  |     argument requires that `'a` must outlive `'static`

error: lifetime may not live long enough
  --> src/lib.rs:10:13
   |
9  | fn bad1<'a>(value: &'a str) {
   |         -- lifetime `'a` defined here
10 |     let _p: &'static str = value;
   |             ^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`

error: lifetime may not live long enough
  --> src/lib.rs:15:19
   |
14 | fn bad2<'a>(value: &'a str) {
   |         -- lifetime `'a` defined here
15 |     let phantom = PhantomData::<*mut &'static str>;
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'static`
   |
   = note: requirement occurs because of a mutable pointer to `&str`
   = note: mutable pointers are invariant over their type parameter
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
Desired output
error: lifetime may not live long enough
 --> src/lib.rs:2:5
  |
1 | fn good1<'a>(value: &'a str) -> &'static str {
  |          -- lifetime `'a` defined here
2 |     value
  |     ^^^^^ returning this value requires that `'a` must outlive `'static`

error[E0521]: borrowed data escapes outside of function
 --> src/lib.rs:6:5
  |
5 | fn good2<'a>(value: &'a str) {
  |          --  ----- `value` is a reference that is only valid in the function body
  |          |
  |          lifetime `'a` defined here
6 |     core::convert::identity::<&'static str>(value);
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |     |
  |     `value` escapes the function body here
  |     argument requires that `'a` must outlive `'static`

error: lifetime may not live long enough
  --> src/lib.rs:10:13
   |
9  | fn bad1<'a>(value: &'a str) {
   |         -- lifetime `'a` defined here
10 |     let _p: &'static str = value;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |     |
   |     type annotation requires that `'a` must outlive `'static`

error: lifetime may not live long enough
  --> src/lib.rs:15:19
   |
14 | fn bad2<'a>(value: &'a str) {
   |         -- lifetime `'a` defined here
15 |     let phantom = PhantomData::<*mut &'static str>;
16 |     unify(phantom, value);
   |     ^^^^^^^^^^^^^^^^^^^^^^
   |     |
   |     `value` escapes the function body here
   |     argument requires that `'a` must outlive `'static`
   |
   = note: requirement occurs because of a mutable pointer to `&str`
   = note: mutable pointers are invariant over their type parameter
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
Rationale and extra context

It's strange that the span sometimes points at the type or expression that implies the 'static bound, rather than at the expression that doesn't satisfy the 'static bound. It seems to me that it'd be more correct to always point at the value/type with the shortest lifetime first, and then at the reason why that lifetime was insufficient second.

good1 and bad1 are very similar: in both cases, a value is checked against a type annotation. But while good1 points at the expression with a too-short lifetime, bad2 spells nonsense like "type annotation requires that 'a must outlive 'static" -- at the very least, the type annotation doesn't require anything, value being assigned to a variable of this type does.

In good2 and bad2, it's inconsistent that f::<'static>(value) believes value is the problem, while f([a marker struct with a 'static lifetime parameter], value) is angry at the marker struct, even though in both cases, the lifetime bound is semantically provided "out of band". Replacing let with const in bad2 results in much better output, though obviously only because of changes in type inference.

My main problem with the current diagnostic is that, if a variable is assigned multiple times or a marker struct is used in multiple unify calls, it's impossible to find exactly which assignment or call caused the issue.

Other cases

Rust Version
rustc 1.90.0-nightly (a00149764 2025-07-14)
binary: rustc
commit-hash: a001497644bc229f1abcc5b2528733386591647f
commit-date: 2025-07-14
host: x86_64-unknown-linux-gnu
release: 1.90.0-nightly
LLVM version: 20.1.8
Anything else?

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the four Rust examples with the reported rustc 1.90.0-nightly version and compare the current diagnostics with the desired output. Trace the compiler diagnostic handling for lifetime errors and adjust the relevant tests or diagnostic cases so spans identify the failing value or call, then verify the examples produce the requested messages.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.