Unable to tokio::run a boxed Future because the tr

2019-08-20 09:04发布

问题:

This question already has an answer here:

  • The trait bound `futures::Future<Item=Arc<T>, Error=Box<Error + Send>>: Send` is not satisfied 1 answer
  • Sending trait objects between threads in Rust 1 answer

I have a function that should optionally run a future or do nothing, depending on parameters. I tried putting a Box around the two futures that will be returned, a tokio::prelude::future::Done<Item=(), Error=()> that immediately resolves to Ok(()), and a tokio::timer::Delay that I'm using and_then and map_err to turn both the Item and Error to (). This doesn't seem to work for me when I try to run the futures with tokio::run.

extern crate tokio;

use std::time::{Duration, Instant};
use tokio::prelude::*;
use tokio::timer;

fn main() {
    tokio::run(foo(12));
}

fn foo(x: i32) -> Box<Future<Item = (), Error = ()>> {
    if x == 0 {
        Box::new(
            timer::Delay::new(Instant::now() + Duration::from_secs(5))
                .and_then(|_| Ok(()))
                .map_err(|_| ()),
        )
    } else {
        Box::new(future::result(Ok(())))
    }
}

This fails to compile with the following error message:

error[E0277]: the trait bound `tokio::prelude::Future<Error=(), Item=()>: std::marker::Send` is not satisfied
 --> src/main.rs:8:5
  |
8 |     tokio::run(foo(12));
  |     ^^^^^^^^^^ `tokio::prelude::Future<Error=(), Item=()>` cannot be sent between threads safely
  |
  = help: the trait `std::marker::Send` is not implemented for `tokio::prelude::Future<Error=(), Item=()>`
  = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<tokio::prelude::Future<Error=(), Item=()>>`
  = note: required because it appears within the type `std::boxed::Box<tokio::prelude::Future<Error=(), Item=()>>`
  = note: required by `tokio::run`

It appears that Box<Future...> does not implement Send, which doesn't make sense to me. Since the Future types that I'm returning both implement Send, it seems to me that the Box should, since impl Send for Box<T> where T: Send is an auto implement in the stdlib. What am I missing here?

回答1:

I realized that I need to specify in the return type to Foo that the future is Send. This compiles:

extern crate tokio;

use std::time::{Duration, Instant};
use tokio::prelude::*;
use tokio::timer;

fn main() {
    tokio::run(foo(12));
}

fn foo(x: i32) -> Box<Future<Item = (), Error = ()> + Send> { // note the + Send at the end of this line
    if x == 0 {
        Box::new(
            timer::Delay::new(Instant::now() + Duration::from_secs(5))
                .and_then(|_| Ok(()))
                .map_err(|_| ()),
        )
    } else {
        Box::new(future::result(Ok(())))
    }
}