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

Unhelpful error "one type is more general than the other" in async code #64650

Open
Tracked by #110338
Seeker14491 opened this issue Sep 21, 2019 · 8 comments
Open
Tracked by #110338
Labels
A-async-await Area: Async & Await A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@Seeker14491
Copy link
Contributor

The following code does not compile:

// Crate versions:
// futures-preview v0.3.0-alpha.18
// runtime v0.3.0-alpha.7

use futures::{prelude::*, stream::FuturesUnordered};

#[runtime::main]
async fn main() {
    let entries: Vec<_> = vec![()]
        .into_iter()
        .map(|x| async { vec![()].into_iter().map(|_| x) })
        .collect::<FuturesUnordered<_>>()
        .map(stream::iter)
        .flatten()
        .collect()
        .await;
}

It produces this error message:

error[E0308]: mismatched types
 --> src\main.rs:3:1
  |
3 | #[runtime::main]
  | ^^^^^^^^^^^^^^^^ one type is more general than the other
  |
  = note: expected type `std::ops::FnOnce<(std::iter::Map<std::vec::IntoIter<()>, [closure@src\main.rs:7:51: 7:56 x:&()]>,)>`
             found type `std::ops::FnOnce<(std::iter::Map<std::vec::IntoIter<()>, [closure@src\main.rs:7:51: 7:56 x:&()]>,)>`

In this case, the code can be made to compile by adding move in two places on this line:

// ...
        .map(|x| async move { vec![()].into_iter().map(move |_| x) })
// ...

Some possibly related issues: #57362, #60658

rustc version: 1.39.0-nightly (97e58c0d3 2019-09-20)

@jonas-schievink jonas-schievink added A-async-await Area: Async & Await A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 21, 2019
@nikomatsakis nikomatsakis added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Oct 1, 2019
@Seeker14491
Copy link
Contributor Author

Here's an updated reproduction that doesn't use the runtime crate:

// Using futures-preview v0.3.0-alpha.19
use futures::StreamExt;

fn main() {
    enter(run())
}

async fn run() {
    let entries: Vec<_> = vec![()]
        .into_iter()
        .map(|x| async { vec![()].into_iter().map(|_| x) })
        .collect::<futures::stream::FuturesUnordered<_>>()
        .map(futures::stream::iter)
        .flatten()
        .collect()
        .await;
}

pub fn enter(x: impl Send) {}

Error:

error[E0308]: mismatched types
 --> src\main.rs:5:5
  |
5 |     enter(run())
  |     ^^^^^ one type is more general than the other
  |
  = note: expected type `std::ops::FnOnce<(std::iter::Map<std::vec::IntoIter<()>, [closure@src\main.rs:11:51: 11:56 x:&()]>,)>`
             found type `std::ops::FnOnce<(std::iter::Map<std::vec::IntoIter<()>, [closure@src\main.rs:11:51: 11:56 x:&()]>,)>`

ructc version: rustc 1.40.0-nightly (22bc9e1d9 2019-09-30)

@estebank estebank added D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-confusing Diagnostics: Confusing error or lint that should be reworked. labels Feb 22, 2020
@djc
Copy link
Contributor

djc commented Mar 8, 2020

Someone was just asking about this in hyper's gitter channel. The diagnostics here are surprisingly bad. Ping?

@Aaron1011
Copy link
Member

Aaron1011 commented Mar 18, 2020

The output is better on nightly:

error[E0597]: `x` does not live long enough
  --> src/main.rs:10:55
   |
10 |         .map(|x| async { vec![()].into_iter().map(|_| x) })
   |                        -------------------------------^---
   |                        |                              |  |
   |                        |                              |  `x` dropped here while still borrowed
   |                        |                              borrowed value does not live long enough
   |                        value captured here by generator

error[E0308]: mismatched types
 --> src/main.rs:4:5
  |
4 |     enter(run())
  |     ^^^^^ one type is more general than the other
  |
  = note: expected type `std::ops::FnOnce<(std::iter::Map<std::vec::IntoIter<()>, [closure@src/main.rs:10:51: 10:56 x:&()]>,)>`
             found type `std::ops::FnOnce<(std::iter::Map<std::vec::IntoIter<()>, [closure@src/main.rs:10:51: 10:56 x:&()]>,)>`

error: aborting due to 2 previous errors

but still contains the spurious "mismatched types" error.

@Aaron1011
Copy link
Member

I think the 'one type is more general than the other' error is another instance of #64552

@Nashenas88
Copy link
Contributor

Nashenas88 commented Oct 13, 2020

This doesn't include async, but the conditions are similar enough, and I found it equally confusing (especially once I realized what the fix was): https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f2cf25b58f393f4a4377b0086e0032a2

inline:

#[derive(Clone)]
struct A;

trait CloneableFn: Fn(&A) + CloneBox {}

trait CloneBox {
    fn clone_box(&self) -> Box<dyn CloneableFn>;
}

impl<T> CloneableFn for T where T: Fn(&A) + CloneBox {}

impl<T> CloneBox for T where T: Fn(&A) + Clone + 'static {
    fn clone_box(&self) -> Box<dyn CloneableFn> {
        Box::new(self.clone()) as Box<dyn CloneableFn>
    }
}


fn take_cfn(cfn: Box<dyn CloneableFn>, a: A) {
    cfn(&a);
}

fn main() {
    let a = A;
    let closure = move |_| { // solve with `move |_: &A|`
        let _b = a.clone();
    };
    take_cfn(Box::new(closure) as Box<dyn CloneableFn>, A);
}

Error:

error[E0308]: mismatched types
  --> src/main.rs:28:14
   |
28 |     take_cfn(Box::new(closure) as Box<dyn CloneableFn>, A);
   |              ^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected type `for<'r> std::ops::Fn<(&'r A,)>`
              found type `std::ops::Fn<(&A,)>`

@jerel
Copy link

jerel commented Jan 12, 2021

I ran into this same issue this week using tokio. Here's my conclusion, in case it's helpful for narrowing in on the bug.

Reduced down to as few lines as possible it looks like this:

runtime.spawn(async {
  warp::serve(
    routes
      .or(other_routes)
      .or(more_routes)
      .map(|reply| {
        warp::reply::with_header(reply, "Sec-WebSocket-Protocol", "graphql-ws")
      })
  )
  .run(([0, 0, 0, 0], 3001))
  .await;
});

and fails with:

   --> src/lib.rs:185:14
    |
185 |   runtime.spawn(async {
    |           ^^^^^ one type is more general than the other
    |
    = note: expected type `std::ops::FnOnce<(warp::generic::Either<(warp::generic::Either<(warp::http::Response<std::vec::Vec<u8>>,), (warp::http::Response<std::vec::Vec<u8>>,)>,), (impl warp::Reply,)>,)>`
               found type `std::ops::FnOnce<(warp::generic::Either<(warp::generic::Either<(warp::http::Response<std::vec::Vec<u8>>,), (warp::http::Response<std::vec::Vec<u8>>,)>,), (impl warp::Reply,)>,)>`

Those types are identical aren't they? I managed to work around it like this:

+ .with(warp::reply::with::header("Sec-WebSocket-Protocol", "graphql-ws"))
- .map(|reply| {
-   warp::reply::with_header(reply, "Sec-WebSocket-Protocol", "graphql-ws")
- })

The first version has been working fine in another part of the codebase, something about how it's called here triggers it maybe? The other way I'm using it is in a #[tokio::main] function instead of runtime.spawn but the implementation is identical.

@Shizcow
Copy link

Shizcow commented Jan 25, 2021

I have found a fairly minimal way to reproduce without external dependencies, and several useful observations. It's not using async, so maybe this is more fitted for #64552?

Tested with rustc 1.51.0-nightly (d98d2f57d 2021-01-18)

This is the reproduction code:

pub struct BoxHolder<BoxedFn> {
    data: BoxedFn,
}

impl<Ret> BoxHolder<Box<fn(&str) -> Ret>> {
    pub fn box_and_set_data(&mut self, ptr: fn(&str) -> Ret) {
        let pre_boxed: Box<fn(&str) -> Ret> = Box::new(ptr); // box contents have concrete size, type annotations are for clarity
        self.data = pre_boxed; // not the case here
    }
}

fn a(a: &str) -> &str {
    ""
}

fn main() {
    let mut foo: BoxHolder<Box<fn(&str) -> &str>> = BoxHolder { data: Box::new(a) };
    foo.box_and_set_data(a);
}

The above yields the following error:

error[E0308]: mismatched types
  --> src/main.rs:19:9
   |
19 |     foo.box_and_set_data(a);
   |         ^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected mutable reference `&mut BoxHolder<Box<for<'r> fn(&'r str) -> &str>>`
              found mutable reference `&mut BoxHolder<Box<for<'r> fn(&'r str) -> &'r str>>`

Replacing the generic Ret with &str does let the code compile, but I have some more observations that more naturally lead to this conclusion. Besides, non-generic fn return parameters aren't fun!

From the error it looks like a lifetime is either forgotten or elided. Removing the type annotations on foo gives a little more data:

fn main() {
    // let mut foo: BoxHolder<Box<fn(&str) -> &str>> = BoxHolder { data: Box::new(a) };
    let mut foo = BoxHolder { data: Box::new(a) };
    foo.box_and_set_data(a);
}

And the new error:

error[E0599]: no method named `box_and_set_data` found for struct `BoxHolder<Box<for<'r> fn(&'r str) -> &'r str {a}>>` in the current scope
  --> src/main.rs:18:9
   |
1  | pub struct BoxHolder<BoxedFn> {
   | ----------------------------- method `box_and_set_data` not found for this
...
18 |     foo.box_and_set_data(a);
   |         ^^^^^^^^^^^^^^^^ method not found in `BoxHolder<Box<for<'r> fn(&'r str) -> &'r str {a}>>`

Well that's not right. a is supposed to be for<'r> fn(&'r str) -> &'static str, not for<'r> fn(&'r str) -> &'r str. The 'static seems to be replaced by 'r.

What explicitly declaring the lifetime of a's return:

// fn a(a: &str) -> &str {
fn a(a: &str) -> &'static str {
    ""
}

fn main() {
    let mut foo = BoxHolder { data: Box::new(a) };
    foo.box_and_set_data(a);
}

New error:

error[E0599]: no method named `box_and_set_data` found for struct `BoxHolder<Box<for<'r> fn(&'r str) -> &'static str {a}>>` in the current scope
  --> src/main.rs:18:9
   |
1  | pub struct BoxHolder<BoxedFn> {
   | ----------------------------- method `box_and_set_data` not found for this
...
18 |     foo.box_and_set_data(a);
   |         ^^^^^^^^^^^^^^^^ method not found in `BoxHolder<Box<for<'r> fn(&'r str) -> &'static str {a}>>`

Now the lifetimes are correct but it's not picking up the impl, even though impl<Ret> is about as general as you can get. What about explicitly declaring the impl with the compiler-supplied type, but still with generic return:

impl BoxHolder<Box<for<'r> fn(&'r str) -> &'static str>> {
    pub fn box_and_set_data(&mut self, ptr: for<'r> fn(&'r str) -> &'static str) {
        self.data = Box::new(ptr);
    }
}

fn a(a: &str) -> &'static str {
    ""
}

fn main() {
    let mut foo = BoxHolder { data: Box::new(a) };
    foo.box_and_set_data(a);
}

New error:

error[E0599]: no method named `box_and_set_data` found for struct `BoxHolder<Box<for<'r> fn(&'r str) -> &'static str {a}>>` in the current scope
  --> src/main.rs:17:9
   |
1  | pub struct BoxHolder<BoxedFn> {
   | ----------------------------- method `box_and_set_data` not found for this
...
17 |     foo.box_and_set_data(a);
   |         ^^^^^^^^^^^^^^^^ method not found in `BoxHolder<Box<for<'r> fn(&'r str) -> &'static str {a}>>`

Even though the impl Type is the exact same type as in the error, it's not recognized. The error is the same if the additional lifetime parameters are left out (IE impl BoxHolder<Box<fn(&str) -> &str>>). The only thing I've gotten to work is the following:

pub struct BoxHolder<BoxedFn> {
    data: BoxedFn,
}

impl BoxHolder<Box<fn(&str) -> &str>> {
    pub fn box_and_set_data(&mut self, ptr: fn(&str) -> &str) {
        self.data = Box::new(ptr);
    }
}

fn a(a: &str) -> &str {
    ""
}

fn main() {
    let mut foo: BoxHolder<Box<fn(&str) -> &str>> = BoxHolder { data: Box::new(a) };
    foo.box_and_set_data(a);
}

The above code compiles correctly. It seems that BoxHolder { data: Box::new(a) } is not BoxHolder<Box<fn(&str) -> &str>> but can be imlpicitly converted into it.

FWIW, doing type F = fn(&str) -> &str; and using F everywhere doesn't change the errors. An implicit cast to BoxHolder<Box<F>> is still required. If someone knows how to directly check the type of two variables (during compilation?) I think the next step would be seeing what the types of the following two variables are, because the type system doesn't seem to think they're equal according to the testing above:

fn a(a: &str) -> &str {
    ""
}

fn main() {
    let a1 = a;
    let a2: fn(&str) -> &str = a;
}

This is all the useful information I'm able to gleam from this. I'm unsure if the later results are directly related to the initial one type is more general than the other issue, if they're another bug entirely, or if they're a thing because I just plain don't know enough rust. Hope it helps in diagnosing the root cause.

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 A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

10 participants