7

Cross posting on github

Given the following snippet:

use futures::stream::{self, StreamExt};

async fn from_bar(bar: &[Vec<&u8>]) {
    let x = bar.iter().flat_map(|i| i.iter().map(|_| async { 42 }));
    let foo: Vec<_> = stream::iter(x).collect().await;
}

#[tokio::main]
async fn main() {
    for bar in vec![] {
        tokio::spawn(async {
            from_bar(bar).await;
        });
    }
}

I get the following errors:

error[E0308]: mismatched types
  --> src/main.rs:11:9
   |
11 |         tokio::spawn(async {
   |         ^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected type `std::ops::FnOnce<(&&u8,)>`
              found type `std::ops::FnOnce<(&&u8,)>`

error: implementation of `std::iter::Iterator` is not general enough
    --> src/main.rs:11:9
     |
11   |           tokio::spawn(async {
     |           ^^^^^^^^^^^^ implementation of `std::iter::Iterator` is not general enough
     |
     = note: `std::iter::Iterator` would have to be implemented for the type `std::slice::Iter<'0, &u8>`, for any lifetime `'0`...
     = note: ...but `std::iter::Iterator` is actually implemented for the type `std::slice::Iter<'1, &u8>`, for some specific lifetime `'1`

I was expecting no error because the lifetimes seem to be correct to me. Note that removing main() or removing the code inside from_bar() both eliminate the errors. Not only that, the error messages are also very strange. They may be related to a regression in the compiler, though more than that they seem to be in the wrong place (maybe related).

Version rustc 1.43.0 (4fb7144ed 2020-04-20):

[dependencies]
futures = '0.3.1'

[dependencies.tokio]
version = '0.2'
features = ['full']
Stargateur
  • 24,473
  • 8
  • 65
  • 91

1 Answers1

-1

Slightly simpler reproduction:

use futures::stream::{self, StreamExt};

async fn from_bar(bar: &[Vec<&u8>]) {
    let x = bar.iter().flat_map(|i| i.iter().map(|_| async { 42 }));
    let foo: Vec<_> = stream::iter(x).collect().await;
}

#[tokio::main]
async fn main() {
    tokio::spawn(async {
        from_bar(&vec![]).await;
    });
}

playground.

This example makes what's wrong clearer: the reference that you pass to from_bar only lives until the end of main (or possibly even the current loop iteration in your example), but spawn needs it to live longer than that since the spawned task may run longer.

What I don't understand is how this translates into the error message we get…

Jmb
  • 18,893
  • 2
  • 28
  • 55
  • This doesn't preserve the semantics of what OP was doing; he seems to want to apply `from_bar` iteratively to every element of a `Vec`. Edit: This is a case of an unhelpful error message, and there's an [open issue](https://github.com/rust-lang/rust/issues/64650) out about it. Please see my answer specifying this below. – Hari Amoor Apr 29 '20 at 06:35
  • @HariAmoor the question wasn't "How to make this work", but "Why is this an error". My answer reproduces the same error and explains why it there is an error. – Jmb Apr 29 '20 at 07:03
  • no the vector is consume, the object is moved in the async block. The code compile fine with imperative style so it's not a lifetime problem. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=329421030c183f9db057e0f640a17a0c – Stargateur Apr 29 '20 at 10:13
  • The async block is only evaluated inside the `spawn`, since `async` blocks are lazy. Therefore Jmb is right - this wouldn't make sense anyway. I think the modified example might work because it infers from the requirements that the arguments passed to `from_bar` must be static (&'static [Vec<&'static u8>])`. And that `vec![]` generates an empty vector of static references which also satisfies that criteria. – Matthias247 Sep 16 '20 at 06:44