这是indexloc提供的服务,不要输入任何密码
Skip to content

error[E0581] gives incomplete help and explanations with TAIT #144432

@QuineDot

Description

@QuineDot

Code

#![feature(type_alias_impl_trait)]
use std::fmt::Display;

pub type Tait<'a> = impl Display;
#[define_opaque(Tait)]
fn define(s: &str) -> Tait<'_> {
    s
}

trait Trait {
    async fn method(&self, tait: Tait<'_>);
    async fn method_2<'t>(&self, tait: Tait<'t>);
    async fn this_one_works<'t: 't>(&self, tait: Tait<'t>);
    fn this_also_works(&self, tait: Tait<'_>)
        -> impl Future<Output = ()> + use<'_, Self>;
}

Current output

error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types
  --> src/lib.rs:11:5
   |
11 |     async fn method(&self, tait: Tait<'_>);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: lifetimes appearing in an associated or opaque type are not considered constrained
   = note: consider introducing a named lifetime parameter

error[E0581]: return type references lifetime `'t`, which is not constrained by the fn input types
  --> src/lib.rs:12:5
   |
12 |     async fn method_2<'t>(&self, tait: Tait<'t>);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0581`.

Desired output

error[E0581]: return type captures an anonymous late-bound lifetime, which is not constrained by the fn input types
  --> src/lib.rs:11:5
   |
11 |     async fn method(&self, tait: Tait<'_>);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^
   |                                       |
   |                                       the unconstrained lifetime
   |
   = note: lifetimes appearing in an associated or opaque types such as `Tait<'_>` are not considered constrained
   = note: `async fn` return a Future which captures all input lifetimes
   = help: consider introducing a named lifetime parameter with a bound to make it early-bound (see E0794 to read more about late and early bound parameters)

11 |     async fn method<'t: 't>(&self, tait: Tait<'t>);

   = help: considering using `-> impl Future + use<..>`, though this may change the semantics of the method

11 |     fn method(&self, tait: Tait<'_>) -> impl Future<Output = ()> + use<'_, Self>;


error[E0581]: return type captures late-bound lifetime `'t`, which is not constrained by the fn input types
  --> src/lib.rs:12:5
   |
12 |     async fn method_2<'t>(&self, tait: Tait<'t>);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^
   |                                             |
   |                                             the unconstrained lifetime
   = note: lifetimes appearing in an associated or opaque types such as `Tait<'t>` are not considered constrained
   = note: `async fn` return a Future which captures all input lifetimes
   = help: add a bound to `'t` make it early-bound (see E0794 to read more about late and early bound parameters)

12 |     async fn method_2<'t: 't>(&self, tait: Tait<'t>);

   = help: considering using `-> impl Future + use<..>`, though this may change the semantics of the method

12 |     fn method_2<'t>(&self, tait: Tait<'t>) -> impl Future<Output = ()> + use<'_, Self>;

Rationale and extra context

This issue is very related to #135350 and the issues linked to it, but doesn't require a separate implementation.

First considering method: The error talks about the return type, but the return type here is invisible. The entire function is underlined (if this was an RPITIT, the return would be underlined). The on-screen error mainly talks about "constrained", but almost no-one knows what that means. The --explain text says

In a fn type, a lifetime appears only in the return type and not in the arguments types.

But the lifetime does appear in the argument type. It is true that the first note tries to compensate for this over-generalization, but you have to know what an opaque type is. So in the end, there is a lot that the programmer has to put together to figure out what the compiler is complaining about. (There's a hidden return type here, it captures the input lifetimes, those lifetimes have to be "constrained", showing up in the arguments isn't enough to be constrained if an opaque type is involved, the type Tait<'_> = impl ... is such an opaque type.)

The second note says

note: consider introducing a named lifetime parameter

That takes us to method_2. The main error is the same, but now both notes have disappeared. If you happened to start from here, you may not have all the pieces to figure out what the compiler is complaining about. Whether you started here or not, the compiler hasn't given you the actual fix: make the input lifetime early bound.

Late versus early bound is also arcane knowledge, but if it was mentioned, those with enough experience would get a better clue, and those without the arcane knowledge would have a better chance of finding the solution via searching for "early bound".

Better yet would be a structured suggestion like the <'t: 't> I suggested above.

Alternatively precise capturing (and foregoing async fn) can remove the capturing from the return type, but this also changes the semantics of the method.

Using a generic instead of a TAIT may be an option sometimes (but also changes the semantics of the method, may effect dyn compatibility, etc).

Other cases

Rust Version

Playground:
- Build using the Nightly version: 1.90.0-nightly (2025-07-23 ace633090349fc5075b5)

Anything else?

Playground link.

Inspired by an URLO thread. (Although I recognize it's arcane knowledge, I would have solved that thread a lot sooner if the error had just mentioned late/early bound.)

The note:

   = note: lifetimes appearing in an associated or opaque type are not considered constrained

implies there may be a way to trigger this use case on stable (without type_alias_impl_trait) using associated types, but I haven't reproduced such an example so far.

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