Skip to content

"implementation of std::marker::Send is not general enough" when using join_all #1451

Closed
@tux3

Description

@tux3

I'm trying to use join_all to parallelize some async work.
Because my stub function returns a non-Send error type, I consume the error before passing it to join_all using a .map combinator (I have also tried map_err(|e| ())).
The join_all result is then dispatched to tokio:

#![feature(await_macro, async_await, futures_api)]
use futures::future::{FutureExt, TryFutureExt};

/* Cargo.toml dependencies:
tokio = {version = "0.1", features = ["async-await-preview"]}
futures-preview = { version = "0.3.0-alpha" }
*/

async fn stub() -> Result<(), Box<dyn std::error::Error + 'static>> { Ok(()) }

fn main() {
    tokio::run_async(async {
        tokio::await!(futures::future::join_all((1..10).map(|n| {
            stub().map(|r| r.is_ok()).boxed()
        })));
    });
}

This fails badly, with rustc trying to teach me type theory:

error: implementation of `std::marker::Send` is not general enough
  --> src/main.rs:12:5
   |
12 |     tokio::run_async(async {
   |     ^^^^^^^^^^^^^^^^
   |
   = note: `std::marker::Send` would have to be implemented for the type `std::ptr::Unique<futures_util::future::join_all::ElemState<std::pin::Pin<std::boxed::Box<futures_util::try_future::map_err::MapErr<impl core::future::future::Future, [closure@src/main.rs:14:28: 14:34]>>>>>`, for any lifetime `'0`
   = note: but `std::marker::Send` is actually implemented for the type `std::ptr::Unique<futures_util::future::join_all::ElemState<std::pin::Pin<std::boxed::Box<futures_util::try_future::map_err::MapErr<impl core::future::future::Future, [closure@src/main.rs:14:28: 14:34]>>>>>`, for some specific lifetime `'1`

However I eventually figured out that if I replace the call to .map(|r| r.is_ok()) by this fut_is_ok wrapper function, everything is fine:

// [...]

async fn fut_is_ok(f: impl std::future::Future<Output=Result<(), Box<dyn std::error::Error + 'static>>>) -> bool {
    await!(f).is_ok()
}

fn main() {
    tokio::run_async(async {
        tokio::await!(futures::future::join_all((1..10).map(|n| {
            fut_is_ok(stub()).boxed()
        })));
    });
}

At this point I am already thoroughly confused, but to add to the fun the following things also make the error go away:

  • Making stub return Result<(), ()> (without removing the map call)
  • Making stub return a Send error, but only if the map call is removed
  • Awaiting only one future instead of doing a join_all

I suspect that somewhere deep in its type parameters the combinator version is still somehow keeping track of the non-Send type I'm trying to map away, whereas the free function does not.

Is this error about std::marker::Send expected behavior, and is there some core async/await concept I'm grossly misunderstanding here?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions