rust-lang / rust-lang/rust

Incorrect "implementation not general enough" error in async block

Open
#153,638 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-async-await C-bug needs-triage T-compiler
Dominant language
Rust
Stars
119k
Forks
16.2k
PR merge metrics
PR metrics pending

Description

Let's say we have this API for a push-based async stream of elements:

/// A sequence of zero or more asynchronously produced elements. Elements are accepted by an
/// `Acceptor`, which can push back on production of the stream while it processes each element.
pub trait Stream {
    type Element: Send;

    fn consume(
        self,
        acceptor: &mut impl Acceptor<Self::Element>,
    ) -> impl Future<Output = ()> + Send;
}

/// Knows how to accept values from a stream that produces elements of type `E`.
pub trait Acceptor<E>: Send {
    fn accept(&mut self, element: E) -> impl Future<Output = ()> + Send;
}

Then we can provide this async function to collect a stream into a vector:

/// Consume the supplied stream, returning a vector of all of its elements in order.
pub async fn collect_stream<S>(s: S) -> Vec<S::Element>
where
    S: Stream,
{
    struct VecAcceptor<E>(Vec<E>);

    impl<E> Acceptor<E> for VecAcceptor<E>
    where
        E: Send,
    {
        async fn accept(&mut self, element: E) {}
    }

    let mut acceptor = VecAcceptor(Vec::new());
    s.consume(&mut acceptor).await;
    acceptor.0
}

However, if we call that function in this very specific context (playground), then we get a nonsense error about trait implementations not being general enough:

fn accept_future<F>(future: F)
where
    F: Future,
    F: Send,
{
    todo!();
}

fn call_it() {
    struct S<'a> {
        element_0: &'a i32,
    }

    impl<'a> Stream for S<'a> {
        type Element = &'a i32;

        async fn consume(self, acceptor: &mut impl Acceptor<Self::Element>) {
            acceptor.accept(self.element_0).await;
        }
    }

    accept_future(async move {
        let n0 = 17;
        let _ = collect_stream(S { element_0: &n0 }).await;
    });
}
error: implementation of `Acceptor` is not general enough
  --> src/lib.rs:61:5
   |
61 | /     accept_future(async move {
62 | |         let n0 = 17;
63 | |         let _ = collect_stream(S { element_0: &n0 }).await;
64 | |     });
   | |______^ implementation of `Acceptor` is not general enough
   |
   = note: `VecAcceptor<&'0 i32>` must implement `Acceptor<&'1 i32>`, for any two lifetimes `'0` and `'1`...
   = note: ...but it actually implements `Acceptor<&i32>`

error: implementation of `Stream` is not general enough
  --> src/lib.rs:61:5
   |
61 | /     accept_future(async move {
62 | |         let n0 = 17;
63 | |         let _ = collect_stream(S { element_0: &n0 }).await;
64 | |     });
   | |______^ implementation of `Stream` is not general enough
   |
   = note: `Stream` would have to be implemented for the type `S<'_>`
   = note: ...but `Stream` is actually implemented for the type `S<'0>`, for some specific lifetime `'0`

This is very specific to the calling context. For example if we remove the Send bound on accept_future then it works, and it also works if you send the stream through a generic identity function before it goes to collect_stream. It seems to also require the presence of an async block; an async function is not enough to trigger it.

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 linked Rust Playground reproducer and compare it with the variants that remove the Send bound or pass the stream through an identity function. Investigate how the async block, Send requirement, and lifetime-general trait obligations produce the diagnostic. Done means the specific case no longer reports an incorrect “implementation not general enough” error, without regressing the contrasting cases.

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
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.