Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weirdness around async function with error "implementation of FnOnce is not general enough" #114177

Open
Tracked by #110338
e00E opened this issue Jul 28, 2023 · 1 comment
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@e00E
Copy link
Contributor

e00E commented Jul 28, 2023

// Using `impl Future` instead of `async to ensure that the Future is Send.
//
// In the original code `a` would be `&[T]`. For more minimization I've removed the reference.
fn foo(a: [(); 0]) -> impl std::future::Future<Output = ()> + Send {
    async move {
        let iter = Adaptor::new(a.iter().map(|_: &()| {}));
        std::future::pending::<()>().await;
    }
}

struct Adaptor<T: Iterator> {
    iter: T,
    v: T::Item,
}

impl<T: Iterator> Adaptor<T> {
    pub fn new(_: T) -> Self {
        Self {
            iter: todo!(),
            v: todo!(),
        }
    }
}

This code is the result of minimizing a compiler error in an async function using Itertools::unique. Compiling this code is weird in several ways:

1:
Compilation fails with this message:

error: implementation of `Iterator` is not general enough
 --> src/lib.rs:5:5
  |
5 | /     async move {
6 | |         let iter = Adaptor::new(a.iter().map(|_: &()| {}));
7 | |         std::future::pending::<()>().await;
8 | |     }
  | |_____^ implementation of `Iterator` is not general enough
  |
  = note: `Iterator` would have to be implemented for the type `std::slice::Iter<'0, ()>`, for any lifetime `'0`...
  = note: ...but `Iterator` is actually implemented for the type `std::slice::Iter<'1, ()>`, for some specific lifetime `'1`

error: implementation of `FnOnce` is not general enough
 --> src/lib.rs:5:5
  |
5 | /     async move {
6 | |         let iter = Adaptor::new(a.iter().map(|_: &()| {}));
7 | |         std::future::pending::<()>().await;
8 | |     }
  | |_____^ implementation of `FnOnce` is not general enough
  |
  = note: closure with signature `fn(&'0 ())` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
  = note: ...but it actually implements `FnOnce<(&(),)>`

This message is confusing. Why are there two lifetimes? What does the "actually implements" line mean? How can I fix it?

2:
The code compiles when removing the Send bound. This is confusing because the error message does not mention Send. Why does the lifetime error message cause the Future to not be Send? Why do the following changes to the code fix the lifetime error and make the Future Send?

3:
The code can be made to compile by moving the inline closure into a separate variable:

        let f = |_: &()| {};
        // or
        fn f(_: &()) {}
        let iter = Adaptor::new(a.iter().map(f));

This is surprising. Why does inlining the definition of the closure cause the code to not compile?

4:
The code can be made to compile by not holding the iterator across the await point:

        {
            let iter = Adaptor::new(a.iter().map(|_: &()| {}));
        }
        std::future::pending::<()>().await;

This is surprising because the error message does not indicate the lifetime of the iterator itself is a problem.

  1. Trying to do the same thing by dropping the iterator does not work.
        let iter = Adaptor::new(a.iter().map(|_: &()| {}));
        std::mem::drop(iter);
        std::future::pending::<()>().await;

Why does it make a difference whether there is a {} block or a manual drop?

@e00E e00E added the C-bug Category: This is a bug. label Jul 28, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jul 28, 2023
@Noratrieb Noratrieb added T-types Relevant to the types team, which will review and decide on the PR/issue. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Jul 30, 2023
@Enselic Enselic added the A-async-await Area: Async & Await label Jul 29, 2024
@traviscross
Copy link
Contributor

@rustbot labels +AsyncAwait-Triaged

This is another case of:

@rustbot rustbot added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Aug 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants