Rust: expected type [X], but found type [X]

Viewed 89

What does it mean when Rust complains that two equal types don't match?

The following error appears to be comparing the type...

<for<'_> fn(&u32) -> impl futures::Future<Output = u32> {f} as FnOnce<(&u32,)>>::Output

...to itself.

error[E0308]: mismatched types
  --> src/main.rs:31:18
   |
31 |     let output = map(f, input);
   |                  ^^^ lifetime mismatch
   |
   = note: expected associated type `<for<'_> fn(&u32) -> impl futures::Future<Output = u32> {f} as FnOnce<(&u32,)>>::Output`
              found associated type `<for<'_> fn(&u32) -> impl futures::Future<Output = u32> {f} as FnOnce<(&u32,)>>::Output`
   = note: the required lifetime does not necessarily outlive the empty lifetime
note: the lifetime requirement is introduced here
  --> src/main.rs:6:39
   |
6  | pub fn map<U, V, W>(f: impl Fn(&U) -> W, items: Vec<U>) -> impl futures::Stream<Item = V>
   |                                       ^

I've reduced it to the following minimal example:

use futures::stream::{FuturesUnordered, StreamExt};
use async_stream::stream;

pub fn map<U, V, W>(f: impl Fn(&U) -> W, items: Vec<U>) -> impl futures::Stream<Item = V>
where V: Send, W: futures::Future<Output = V> + Send
{
    stream! {
        let mut futures = FuturesUnordered::new();
        let mut i = 2;
        if 2 <= items.len() {
            futures.push(tokio::spawn(f(&items[0])));
            futures.push(tokio::spawn(f(&items[1])));
            while let Some(result) = futures.next().await {
                let y = result.unwrap();
                yield y;
                futures.push(tokio::spawn(f(&items[i])));
                i += 1
            }
        }
    }
}

#[tokio::main]
async fn main() {
    async fn f(x: &u32) -> u32 {
        x + 1
    }
    let input = vec![1, 2, 3];
    let output = map(f, input);
    futures::pin_mut!(output);
    while let Some(x) = output.next().await {
        println!("{:?}", x);
    }
}
1 Answers

It means they are not equal, only displayed so, and rustc has omitted some important details.

In this case, the important information omitted is one lifetime, and let me annotate it:

expected associated type `<for<'_> fn(&u32) -> impl futures::Future<Output = u32> {f} as FnOnce<(&u32,)>>::Output`
   found associated type `<for<'_> fn(&u32) -> impl futures::Future<Output = u32> + '_ {f} as FnOnce<(&u32,)>>::Output + '_`

Or with names,

expected associated type `<for<'a> fn(&'a u32) -> impl futures::Future<Output = u32> {f} as FnOnce<(&'a u32,)>>::Output`
   found associated type `<for<'a> fn(&'a u32) -> impl futures::Future<Output = u32> + 'a {f} as FnOnce<(&'a u32,)>>::Output + 'a`

This is because the async fn f() is desugared into:

fn f<'a>(x: &'a u32) -> impl futures::Future<Output = u32> + 'a {
    async move { x + 1 }
}

That is, the resulting future depends on the argument's lifetime (because it captures it). However, map() expects it to not, since it is a standalone generic paramater (W).

For more and potential solutions, see Lifetime of a reference passed to async callback.

Related