Skip to content

mismatched_lifetime_syntaxes should just say "hidden lifetime parameters are deprecated" #149668

@cormac-ainc

Description

@cormac-ainc

Code

pub struct Abc {
    a: i32,
}

impl Abc {
    pub fn iter(&self) -> std::slice::Iter<i32> {
        std::slice::from_ref(&self.a).iter()
    }
}

Current output

warning: hiding a lifetime that's elided elsewhere is confusing
 --> src/lib.rs:6:17
  |
6 |     pub fn iter(&self) -> std::slice::Iter<i32> {
  |                 ^^^^^     ^^^^^^^^^^^^^^^^^^^^^ the same lifetime is hidden here
  |                 |
  |                 the lifetime is elided here
  |
  = help: the same lifetime is referred to in inconsistent ways, making the signature confusing
  = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default
help: use `'_` for type paths
  |
6 |     pub fn iter(&self) -> std::slice::Iter<'_, i32> {
  |                                            +++

Desired output

warning: hidden lifetime parameters are deprecated
 --> src/lib.rs:6:17
  |
6 |     pub fn iter(&self) -> std::slice::Iter<i32> {
  |                 ^^^^^     -----------------^--- expected a lifetime parameter here
  |                 |
  |                 a lifetime is elided here
  |
  = help: types with hidden lifetimes look like owned types and are confusing when used with elided lifetimes
  = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default
help: use `'_` for types to show they are borrowed
  |
6 |     pub fn iter(&self) -> std::slice::Iter<'_, i32> {
  |                                            +++

Rationale and extra context

According to the 1.89 release post, the whole rationale for mismatched_lifetime_syntaxes is that we want to deprecate hidden lifetimes entirely (the lint elided_lifetimes_in_paths) but it's too much to do all at once. So there is a clear goal in mind. Rustc has tried to get closer by recognising a difference between hidden and elided lifetimes.

Unfortunately this implementation strategy has leaked into the error message. I think that the error message for mismatched_lifetime_syntaxes is really hard to get your head around. Even if this is a less bothersome/frequent/annoying and more focused/useful lint than elided_lifetimes_in_paths, it is about 20x harder to understand why the compiler is warning you.

One lint is a subset of the other. We can very directly compare them. This is elided_lifetimes_in_paths at 1.91.1, minus the 'where lint is declared' part:

error: hidden lifetime parameters in types are deprecated
 --> thing.rs:2:37
  |
2 | fn yeah(x: &u32) -> std::slice::Iter<u32> {
  |                     ----------------^---- expected lifetime parameter
  |
help: indicate the anonymous lifetime
  |
2 | fn yeah(x: &u32) -> std::slice::Iter<'_, u32> {
  |                                      +++

My reaction to each diagnostic is:

  • elided_lifetimes_in_paths: "hidden lifetime parameters in types are deprecated"
    • Instant comprehension. No new concepts. Very simple remedy: stop hiding your lifetimes in types. It's a simple rule you can follow to write clearer code.
  • mismatched_lifetime_syntaxes: "hiding a lifetime that's elided elsewhere is confusing"
    • I think... what is the difference between hiding and eliding? Which one is which? I would have guessed that these are synonyms.
    • Which the error message has had to prepare for... "this one's hiding, this one's eliding"
    • And then the kicker: Hint: the same lifetime is referred to in inconsistent ways...
    • To which I think... Uh, no, the lifetime is not referred to at all! There is not a single piece of lifetime syntax here.
    • So:

I think we can do better. I think we should just say "hidden lifetimes are deprecated", because it is the attitude you want users to adopt anyway. The way we managed to detect a confusing use of them is not important to the programmer. I think this should be an implementation detail. You can use the other lifetimes it mismatches with to pluck out some context, and that remains useful. I also think the lint can use this to show users why we have decided to deprecate, better than elided_lifetimes_in_paths ever could. I think this phrasing is also less liable to sound judgmental, because "x is deprecated" is not an accusation.

Other background

RalfJung explains hidden vs elided lifetimes in #145312 (comment). It's a fine shade between these two concepts, but sure, it exists.

Other cases

Rust Version

rustc 1.91.1 (ed61e7d7e 2025-11-07)
binary: rustc
commit-hash: ed61e7d7e242494fb7057f2657300d9e77bb4fcb
commit-date: 2025-11-07
host: x86_64-unknown-linux-gnu
release: 1.91.1
LLVM version: 21.1.2

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions