rust-lang / rust-lang/rust

"Impl not general enough" error for using an associated type with drop glue over an await point

Open
#152,850 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

A-async-await C-bug needs-triage WG-async
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I tried this code:

trait Trait<'cx>: Sized {
    type State: Default;
}

struct Problem<'cx, T: Trait<'cx>> {
    state: T::State,
    cx: &'cx (),
}

impl<'cx, T: Trait<'cx>> Problem<'cx, T> {
    fn new() -> Self {
        Self {
            state: Default::default(),
            cx: &(),
        }
    }
}

struct Impler<'cx>(&'cx ());

impl<'cx> Trait<'cx> for Impler<'cx> {
    type State = String;
}

fn problem<'cx>() -> impl Future<Output = ()> + Send {
    async move {
        let _problem: Problem<'cx, Impler<'cx>> = Problem::new();
        async {}.await;
    }
}

I expected to see this happen: successful compilation

Instead, this happened: compilation error

error: implementation of `Trait` is not general enough
  --> src/lib.rs:26:5
   |
26 | /     async move {
27 | |         let _problem: Problem<'cx, Impler<'cx>> = Problem::new();
28 | |         async {}.await;
29 | |     }
   | |_____^ implementation of `Trait` is not general enough
   |
   = note: `Trait<'1>` would have to be implemented for the type `Impler<'0>`, for any two lifetimes `'0` and `'1`...
   = note: ...but `Trait<'2>` is actually implemented for the type `Impler<'2>`, for some specific lifetime `'2`

error: could not compile `tmp` (lib) due to 1 previous error

With -Znext-solver the error is:

error[E0277]: `{coroutine witness@src/lib.rs:26:5: 26:15}` cannot be sent between threads safely
  --> src/lib.rs:25:1
   |
25 | fn problem<'cx>() -> impl Future<Output = ()> + Send {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `{coroutine witness@src/lib.rs:26:5: 26:15}` cannot be sent between threads safely
26 |     async move {
   |     ---------- within this `{async block@src/lib.rs:26:5: 26:15}`
   |
   = help: within `{async block@src/lib.rs:26:5: 26:15}`, the trait `Send` is not implemented for `{coroutine witness@src/lib.rs:26:5: 26:15}`
note: required because it's used within this `async` block
  --> src/lib.rs:26:5
   |
26 |     async move {
   |     ^^^^^^^^^^

error[E0277]: `{coroutine witness@src/lib.rs:26:5: 26:15}` cannot be sent between threads safely
  --> src/lib.rs:26:5
   |
26 |       async move {
   |       ^---------
   |       |
   |  _____within this `{async block@src/lib.rs:26:5: 26:15}`
   | |
27 | |         let _problem: Problem<'cx, Impler<'cx>> = Problem::new();
28 | |         async {}.await;
29 | |     }
   | |_____^ `{coroutine witness@src/lib.rs:26:5: 26:15}` cannot be sent between threads safely
   |
   = help: within `{async block@src/lib.rs:26:5: 26:15}`, the trait `Send` is not implemented for `{coroutine witness@src/lib.rs:26:5: 26:15}`
note: required because it's used within this `async` block
  --> src/lib.rs:26:5
   |
26 |     async move {
   |     ^^^^^^^^^^

error[E0277]: `{coroutine witness@src/lib.rs:26:5: 26:15}` cannot be sent between threads safely
  --> src/lib.rs:25:54
   |
25 |   fn problem<'cx>() -> impl Future<Output = ()> + Send {
   |  ______________________________________________________^
26 | |     async move {
   | |     ---------- within this `{async block@src/lib.rs:26:5: 26:15}`
27 | |         let _problem: Problem<'cx, Impler<'cx>> = Problem::new();
28 | |         async {}.await;
29 | |     }
30 | | }
   | |_^ `{coroutine witness@src/lib.rs:26:5: 26:15}` cannot be sent between threads safely
   |
   = help: within `{async block@src/lib.rs:26:5: 26:15}`, the trait `Send` is not implemented for `{coroutine witness@src/lib.rs:26:5: 26:15}`
note: required because it's used within this `async` block
  --> src/lib.rs:26:5
   |
26 |     async move {
   |     ^^^^^^^^^^

For more information about this error, try `rustc --explain E0277`.
error: could not compile `tmp` (lib) due to 3 previous errors

There are five simple modifications to the code that make it compile (also on `-Znext-solver):

  • remove the Send bound on the output future of fn problem()
  • replace the State of Impler with a type that doesn't need drop glue
  • move the await point above the creation of Problem
  • remove the lifetime of Trait
  • remove the lifetime of Impler

Another modification that makes it compile is to split Trait into a supertrait without a lifetime:

trait Super {
    type State: Default;
}

trait Trait<'cx>: Super + Sized {}

This fix is what I ended up using in my codebase. It also made me think that this might be a compiler bug.

Meta

rustc --version --verbose:

rustc 1.95.0-nightly (842bd5be2 2026-01-29)
binary: rustc
commit-hash: 842bd5be253e17831e318fdbd9d01d716557cc75
commit-date: 2026-01-29
host: x86_64-unknown-linux-gnu
release: 1.95.0-nightly
LLVM version: 22.1.0

(feel free to improve the title)

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

Start with the supplied src/lib.rs reproducer and run it on the reported nightly, both with and without -Znext-solver. Compare the diagnostics across the listed code modifications, then trace the compiler behavior responsible for the await-point, associated-type, and lifetime interaction. Done means the original example compiles as expected without requiring one of the workarounds.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.