Async closure does not have the same type as itself

I'm using the TryStreams trait from the futures library to read files, and I'm hitting a type error that I don't understand.

        let layers = ReadDirStream::new(fs::read_dir(&path).await?)
            .map_err(MyError::from)
            .try_filter(async |dir_entry| dir_entry.path().is_dir());
            .try_filter_map(async |dir_entry| Layer::read(dir_entry.path()).await.map(Some))
            .collect::<Vec<_>>()
            .await;

This is the error I get. My reading is that the async closure on line 86 is not the same type as... the async closure on line 86?

error[E0308]: mismatched types
   --> core/src/layer.rs:84:22
    |
84  |           let layers = ReadDirStream::new(fs::read_dir(&parent).await?)
    |  ______________________^
85  | |             .map_err(MyError::from)
86  | |             .try_filter(async |res| res.path().is_dir())
87  | |             .try_filter_map(async |res| Layer::read(res.path()).await.map(Some))
    | |________________________________________________________________________________^ one type is more general than the other
    |
    = note: expected `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
               found `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
    = note: no two async blocks, even if identical, have the same type
    = help: consider pinning your async block and casting it to a trait object
note: the lifetime requirement is introduced here
   --> /home/chief/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/try_stream/mod.rs:739:32
    |
739 |         F: FnMut(&Self::Ok) -> Fut,
    |                                ^^^

error[E0308]: mismatched types
   --> core/src/layer.rs:84:22
    |
84  |           let layers = ReadDirStream::new(fs::read_dir(&parent).await?)
    |  ______________________^
85  | |             .map_err(MyError::from)
86  | |             .try_filter(async |res| res.path().is_dir())
87  | |             .try_filter_map(async |res| Layer::read(res.path()).await.map(Some))
    | |________________________________________________________________________________^ one type is more general than the other
    |
    = note: expected `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
               found `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
    = note: no two async blocks, even if identical, have the same type
    = help: consider pinning your async block and casting it to a trait object
note: the lifetime requirement is introduced here
   --> /home/chief/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/try_stream/mod.rs:180:25
    |
180 | pub trait TryStreamExt: TryStream {
    |                         ^^^^^^^^^

error[E0308]: mismatched types
  --> core/src/layer.rs:84:22
   |
84 |           let layers = ReadDirStream::new(fs::read_dir(&parent).await?)
   |  ______________________^
85 | |             .map_err(MyError::from)
86 | |             .try_filter(async |res| res.path().is_dir())
87 | |             .try_filter_map(async |res| Layer::read(res.path()).await.map(Some))
   | |________________________________________________________________________________^ one type is more general than the other
   |
   = note: expected `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
              found `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
   = note: no two async blocks, even if identical, have the same type
   = help: consider pinning your async block and casting it to a trait object

error[E0308]: mismatched types
   --> core/src/layer.rs:84:22
    |
84  |           let layers = ReadDirStream::new(fs::read_dir(&parent).await?)
    |  ______________________^
85  | |             .map_err(MyError::from)
86  | |             .try_filter(async |res| res.path().is_dir())
87  | |             .try_filter_map(async |res| Layer::read(res.path()).await.map(Some))
    | |________________________________________________________________________________^ one type is more general than the other
    |
    = note: expected `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
               found `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
    = note: no two async blocks, even if identical, have the same type
    = help: consider pinning your async block and casting it to a trait object
note: the lifetime requirement is introduced here
   --> /home/chief/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/try_stream/mod.rs:779:40
    |
779 |         Fut: TryFuture<Ok = Option<T>, Error = Self::Error>,
    |                                        ^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
   --> core/src/layer.rs:84:22
    |
84  |           let layers = ReadDirStream::new(fs::read_dir(&parent).await?)
    |  ______________________^
85  | |             .map_err(MyError::from)
86  | |             .try_filter(async |res| res.path().is_dir())
87  | |             .try_filter_map(async |res| Layer::read(res.path()).await.map(Some))
    | |________________________________________________________________________________^ one type is more general than the other
    |
    = note: expected `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
               found `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
    = note: no two async blocks, even if identical, have the same type
    = help: consider pinning your async block and casting it to a trait object
note: the lifetime requirement is introduced here
   --> /home/chief/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/try_stream/mod.rs:780:12
    |
780 |         F: FnMut(Self::Ok) -> Fut,
    |            ^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
  --> core/src/layer.rs:84:22
   |
84 |           let layers = ReadDirStream::new(fs::read_dir(&parent).await?)
   |  ______________________^
85 | |             .map_err(MyError::from)
86 | |             .try_filter(async |res| res.path().is_dir())
87 | |             .try_filter_map(async |res| Layer::read(res.path()).await.map(Some))
88 | |             .collect::<Vec<_>>()
   | |________________________________^ one type is more general than the other
   |
   = note: expected `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
              found `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
   = note: no two async blocks, even if identical, have the same type
   = help: consider pinning your async block and casting it to a trait object

error[E0308]: mismatched types
   --> core/src/layer.rs:84:22
    |
84  |           let layers = ReadDirStream::new(fs::read_dir(&parent).await?)
    |  ______________________^
85  | |             .map_err(MyError::from)
86  | |             .try_filter(async |res| res.path().is_dir())
87  | |             .try_filter_map(async |res| Layer::read(res.path()).await.map(Some))
88  | |             .collect::<Vec<_>>()
    | |________________________________^ one type is more general than the other
    |
    = note: expected `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
               found `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
    = note: no two async blocks, even if identical, have the same type
    = help: consider pinning your async block and casting it to a trait object
note: the lifetime requirement is introduced here
   --> /home/chief/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/try_stream/mod.rs:780:31
    |
780 |         F: FnMut(Self::Ok) -> Fut,
    |                               ^^^

error[E0308]: mismatched types
  --> core/src/layer.rs:89:14
   |
86 |             .try_filter(async |res| res.path().is_dir())
   |                                     -------------------
   |                                     |
   |                                     the expected `async` closure body
   |                                     the found `async` closure body
...
89 |             .await;
   |              ^^^^^ one type is more general than the other
   |
   = note: expected `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
              found `async` closure body `{async closure body@core/src/layer.rs:86:37: 86:56}`
   = note: no two async blocks, even if identical, have the same type
   = help: consider pinning your async block and casting it to a trait object

That bound only supports futures that don't capture the borrow (reference) in the input. I believe that your async closure does capture the borrow, and that's the source of the error. (The closure returns an infinite number of futures, type-wise -- a different one for each input lifetime. The bound requires it returns the same type for all input lifetimes.)

The methods you want to call aren't async anyway, so try this:

.try_filter(|dir_entry| {
    let res = dir_entry.path().is_dir();
    async move { res }
})
2 Likes

This is a very unfortunate diagnostic message from the compiler. There are several open issues for that:

2 Likes

This worked perfectly. Thanks for the explanation as well. To restate, the issue is the &self method calls happening inside of the async block, and by moving those outside of the block, the async block captures no references, and thus has no associated lifetimes, correct?

Right, by using the reference inside the async closure, it means the future captures the reference, so you had something analogous to a

// captures lifetime   vv------------------------------------------vvvvvvv
fn closure(dir_entry: &'_ DirEntry) -> impl Future<Ouput = bool> + use<'_>
//                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// This cannot be represented by a generic parameter like -> Fut
// `Fut` has to resolve to a single type -- no free lifetimes

And the change makes it so that the future only captures the bool.

// No longer capturing any lifetimes                               vvvvv
fn closure(dir_entry: &'_ DirEntry) -> impl Future<Ouput = bool> + use<>
//                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// This *can* be represented by a generic parameter like -> Fut

// This fails unless you move the use of the reference to outside
// of the `async` block.  (Or make the return type `+ use<'_>`.)
fn closure(de: &tokio::fs::DirEntry) -> impl Future<Output = bool> + use<> {
    // let res = de.path().is_dir();
    async move {
        let res = de.path().is_dir();
        res
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.