Skip to content

Incorrect "implementation not general enough" error in async block #153638

@jacobsa

Description

@jacobsa

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-async-awaitArea: Async & AwaitC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions