0

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lily Mara
  • 3,859
  • 4
  • 29
  • 48

1 Answers1

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(())))
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lily Mara
  • 3,859
  • 4
  • 29
  • 48
  • 1
    FWIW, in this case you could just use [impl trait and `Either`](https://play.rust-lang.org/?gist=7d8030b979eaa2cdb42def7704124479&version=stable&mode=debug&edition=2015). – Shepmaster Jul 23 '18 at 18:56
  • Thanks @Shepmaster! That's exactly what I wanted. That's much better than boxing. – Lily Mara Jul 23 '18 at 19:26