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

Send marker compile error when trying to use tokio::run() on a trait method returning a Box<Future<...>> #670

Closed
bgbahoue opened this issue Sep 27, 2018 · 6 comments

Comments

@bgbahoue
Copy link

Version 0.1.8

│ │ └── tokio-io v0.1.8
│ ├── tokio v0.1.8
│ │ ├── tokio-codec v0.1.0
│ │ │ └── tokio-io v0.1.8 ()
│ │ ├── tokio-current-thread v0.1.1
│ │ │ └── tokio-executor v0.1.4
│ │ ├── tokio-executor v0.1.4 (
)
│ │ ├── tokio-fs v0.1.3
│ │ │ ├── tokio-io v0.1.8 ()
│ │ │ └── tokio-threadpool v0.1.6
│ │ │ └── tokio-executor v0.1.4 (
)
│ │ │ └── tokio-io v0.1.8 ()
│ │ ├── tokio-io v0.1.8 (
)
│ │ ├── tokio-reactor v0.1.5
│ │ │ ├── tokio-executor v0.1.4 ()
│ │ │ └── tokio-io v0.1.8 (
)
│ │ ├── tokio-tcp v0.1.1
│ │ │ ├── tokio-io v0.1.8 ()
│ │ │ └── tokio-reactor v0.1.5 (
)
│ │ ├── tokio-threadpool v0.1.6 ()
│ │ ├── tokio-timer v0.2.6
│ │ │ └── tokio-executor v0.1.4 (
)
│ │ ├── tokio-udp v0.1.2
│ │ │ ├── tokio-codec v0.1.0 ()
│ │ │ ├── tokio-io v0.1.8 (
)
│ │ │ └── tokio-reactor v0.1.5 ()
│ │ └── tokio-uds v0.2.1
│ │ ├── tokio-io v0.1.8 (
)
│ │ └── tokio-reactor v0.1.5 ()
│ ├── tokio-executor v0.1.4 (
)
│ ├── tokio-io v0.1.8 ()
│ ├── tokio-reactor v0.1.5 (
)
│ ├── tokio-tcp v0.1.1 ()
│ ├── tokio-timer v0.2.6 (
)
│ └── tokio-io v0.1.8 ()
└── tokio v0.1.8 (
)

Platform

Darwin MBPR-de-Boris 17.7.0 Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64

Description

I am trying to build a library defining a generic datasource which can pull data from various sources, synchronously and asynchronously. When building the async piece, I ran into the following compilation issue which I don't understand how to address.

Here is my (simplified) code (playground link)

extern crate futures; // futures = "0.1.24"
extern crate tokio; // tokio = "0.1.8"
extern crate serde_json;

use futures::Future;
use serde_json::Value;

use std::collections::HashMap;

trait DataSource {
    type Data;

    fn read_async(&self, Option<HashMap<String, Value>>) -> Box<futures::Future<Item=Option<Self::Data>, Error=String>> 
        where Self::Data: 'static + Send;
}

struct DataSourceImpl;
impl DataSource for DataSourceImpl {
    type Data = Vec<String>;

    fn read_async(&self, _params: Option<HashMap<String, Value>>) -> Box<futures::Future<Item=Option<Self::Data>, Error=String>> 
        where Self::Data: 'static + Send 
    {
        Box::new(futures::future::ok(Some(vec!["some data".to_string()])))
    }

}

fn main() {
    let datasource = DataSourceImpl{};

    let params = HashMap::new();
    tokio::run(datasource.read_async(Some(params))
        .map(|content| {
            println!("Content read = {:?}", &content);
            ()
        })
        .map_err(|err| {
            println!("Error {}", &err);
            ()
        })
    );
}

I got the following compilation error:

error[E0277]: `dyn futures::Future<Item=std::option::Option<std::vec::Vec<std::string::String>>, Error=std::string::String>` cannot be sent between threads safely
  --> src/main.rs:45:13
   |
45 |     runtime.spawn(future);
   |             ^^^^^ `dyn futures::Future<Item=std::option::Option<std::vec::Vec<std::string::String>>, Error=std::string::String>` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `dyn futures::Future<Item=std::option::Option<std::vec::Vec<std::string::String>>, Error=std::string::String>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<dyn futures::Future<Item=std::option::Option<std::vec::Vec<std::string::String>>, Error=std::string::String>>`
   = note: required because it appears within the type `std::boxed::Box<dyn futures::Future<Item=std::option::Option<std::vec::Vec<std::string::String>>, Error=std::string::String>>`
   = note: required because it appears within the type `futures::Map<std::boxed::Box<dyn futures::Future<Item=std::option::Option<std::vec::Vec<std::string::String>>, Error=std::string::String>>, [closure@src/main.rs:34:14: 37:10]>`
   = note: required because it appears within the type `futures::MapErr<futures::Map<std::boxed::Box<dyn futures::Future<Item=std::option::Option<std::vec::Vec<std::string::String>>, Error=std::string::String>>, [closure@src/main.rs:34:14: 37:10]>, [closure@src/main.rs:38:18: 41:10]>`

Yet when looking into the standard library, I found the following implementations:

  • impl<T: ?Sized> Send for Box<T> where T: Send
  • impl<T> Send for Option<T> where T: Send
  • impl<T> Send for Vec<T> where T: Send
  • impl Send for String

After further testing, I got rid of the Trait and replaced the Box<Future<...>> by impl Future<...>. Then it works (playground link for the new code); yet I don't understand what is wrong with the previous (i.e. Trait & Box) implementation?

extern crate failure;
extern crate futures; // futures = "0.1.24"
extern crate tokio; // tokio = "0.1.8"
extern crate serde_json;

use futures::Future;
use serde_json::Value;

use std::collections::HashMap;

fn read_async(_params: Option<HashMap<String, Value>>) -> impl futures::Future<Item=Option<Vec<String>>, Error=failure::Error> {
    futures::future::ok(Some(vec!["some data".to_string()]))
}

fn main() {
    let params = HashMap::new();
    let future = read_async(Some(params))
        .map(|content| {
            println!("Content read = {:?}", &content);
            ()
        })
        .map_err(|err| {
            println!("Error {}", &err);
            ()
        });

    tokio::run(future);
}
@tobz
Copy link
Member

tobz commented Sep 27, 2018

You need Box<Future<Item=Option<Self::Data>, Error=String> + Send>.

You were close in recognizing that Box<T> should be Send when T is Send, but you just have to spell it out because Future doesn't derive it manually/automatically.

@bgbahoue
Copy link
Author

Indeed, thanks for pointing it out @tobz

Just wondering, is there a good reason for Future<Item=T, _> where T: Send to not automatically derive Send?

@tobz
Copy link
Member

tobz commented Sep 27, 2018

That I personally do not know. :/

Technically, futures are maintained over in the rust-lang-nursery/futures-rs repository. It might be worth asking there, but Tokio uses futures 0.1 which is effectively deprecated, so it really doesn't get much TLC. Not sure how receptive they'd be to such a change, but that is based on the idea that there isn't already a good reason that they didn't add it in the first place. :P

@jeff-hiner
Copy link

I just got hit by this particular footgun. Could someone please update the documentation here? https://tokio.rs/docs/going-deeper/returning/

@carllerche
Copy link
Member

@jeff-at-dwelo I opened tokio-rs/doc-push#87 to track the doc issue.

I would greatly appreciate it if you could take a stab at updating the docs. Note that the "returning futures" section has actually moved (linked in the issue above).

@jeff-hiner
Copy link

Opened tokio-rs/website#247 with some potential changes. It doesn't look like the "returning futures" has moved, as the old section is still there. I tweaked both.

No issue with deleting the old page in a separate PR, but as long as it's there it should be a good example.

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

4 participants