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

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

Closed
tux3 opened this issue Feb 10, 2019 · 2 comments
Closed

Comments

@tux3
Copy link

tux3 commented Feb 10, 2019

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?

@Nemo157
Copy link
Member

Nemo157 commented Feb 10, 2019

Here's a slightly changed example, dropping the Tokio dependency and using current master of futures to avoid needing the extra Box:

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

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

fn assert_send<T: Send>(_: T) {}

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

Then, casting the inner closure into a function pointer gives a more useful error message:

stub().map((|r| r.is_ok()) as fn(Result<(), Box<dyn Error + 'static>>) -> bool)
   = note: `std::marker::Send` would have to be implemented for the type `std::ptr::Unique<[futures_util::future::join_all::ElemState<futures_util::future::map::Map<impl core::future::future::Future, fn(std::result::Result<(), std::boxed::Box<(dyn std::error::Error + '0)>>) -> bool>>]>`, for any lifetime `'0`
   = note: but `std::marker::Send` is actually implemented for the type `std::ptr::Unique<[futures_util::future::join_all::ElemState<futures_util::future::map::Map<impl core::future::future::Future, fn(std::result::Result<(), std::boxed::Box<(dyn std::error::Error + '1)>>) -> bool>>]>`, for some specific lifetime `'1`

So, the lifetime it is complaining about is the lifetime of the input parameter of the closure. Swapping the closure for an identical actual function compiles fine.

No idea where the actual issue is, but it seems likely to be a bug related to rustc's handling of lifetimes on trait object inputs to closures inside async blocks.

@cramertj
Copy link
Member

I think this is rust-lang/rust#64552

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants