Skip to content

Description of E0495 failure difficult to parse #86910

Open
@mcy

Description

@mcy

Given the following code: https://godbolt.org/z/6ndY3zx7q

pub fn next<'buf, 'read, T>(
    r: &'read mut i32,
    f: impl FnOnce(&mut &'read &'buf i32) -> T,
) -> T { todo!() }

pub fn tagged<'buf, 'read, T>(
    r: &'read mut i32,
    f: impl FnOnce(&mut &'read &'buf i32) -> T,
) -> T {
    next(r, move |e| inner(e, f))
}

pub fn inner<'buf, 'read, T>(
    r: &'read mut &&'buf i32,
    f: impl FnOnce(&mut &'read &'buf i32) -> T,
) -> T { todo!() }

The current output is:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'buf` due to conflicting requirements
  --> <source>:10:22
   |
10 |     next(r, move |e| inner(e, f))
   |                      ^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'buf` as defined on the function body at 6:15...
  --> <source>:6:15
   |
6  | pub fn tagged<'buf, 'read, T>(
   |               ^^^^
note: ...but the lifetime must also be valid for the lifetime `'read` as defined on the function body at 6:21...
  --> <source>:6:21
   |
6  | pub fn tagged<'buf, 'read, T>(
   |                     ^^^^^
note: ...so that the types are compatible
  --> <source>:10:22
   |
10 |     next(r, move |e| inner(e, f))
   |                      ^^^^^
   = note: expected `for<'r> FnOnce<(&'r mut &&i32,)>`
              found `for<'r> FnOnce<(&'r mut &'read &'buf i32,)>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
Compiler returned: 1

I run into this (the "bad variance diagonistic") whenever I'm doing anything particularly difficult with lifetimes. Even though most people would say I'm a very advanced Rust user, this particular class of diagnostics is extremely time-consuming for me to unwind. This error always means one of two things:

  • I'm missing an explicit lifetime somewhere. The diagnostic provides no hints as to where it is and I need to hunt through the crate to find it.
  • The API I've designed is impossible to implement because it causes the borrow checker to prove a contradiction.

Both of these are pretty frustrating, and I have to use my personal heuristics to resolve them. I've honestly just gotten used to the pain and am only filing this because @estebank nudged me. I don't want to imagine how a newbie feels when they see this error.

I don't have... obvious suggestions for improving the diagnostic, since I don't actually know how the current borrow checker implementation aggregates information. After all, it would be very hard for it to guess which lifetime I missed, since it would need to explore many more paths.

However, there may be hope. The pretty-printed types are really unhelpful, which points to an underlying issue:

   = note: expected `for<'r> FnOnce<(&'r mut &&i32,)>`  // Lol what's &&i32?
              found `for<'r> FnOnce<(&'r mut &'read &'buf i32,)>`

There's a bunch of different lifetimes with similar names involved. Which 'buf is it referring to? What are the anonymous lifetimes in for<'r> FnOnce<(&'r mut &&i32,)>? The compiler has clearly given these regions anonymous names, so it might be worth highlighting them with something like "Call this region '1" or similar, so that they can be printed in the error?

Perhaps it might also be useful to add a note that lists out the incompatible constraints as a where clause, once we've given the lifetime inference variables names. It would be easier to parse at a glance than trying to build it up from prose.

Finally, I wonder if it's helpful for the compiler to say "btw, I can't do this reduction I wanted to make because this lifetime isn't covariant".

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-varianceArea: Variance (https://doc.rust-lang.org/nomicon/subtyping.html)T-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