Search code examples
rustasync-awaitrust-tokiohyper

New Tokio + dependency with older hyper


I am using tokio 0.2.x but I need to use a dependency that depends on hyper 0.12.35.

This result in SpawnError { is_shutdown: true } panics.

I have managed to reproduce this in an isolated manner:

cargo.toml

[dependencies]
tokio = { version = "0.2.11", features = ["full"] }
hyper = "0.12.35"
futures = "0.1.29"

and this is the code:

use hyper::rt;
use futures::future as future01;

fn future_01() -> impl future01::Future<Item = (), Error = ()> {
    future01::ok(())
}

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    rt::spawn(future_01());
    Ok(())
}

My dependency has this rt::spawn deep into the implementation so that I cannot modify that.

Ideally, I would like to set the default executor used by rt::spawn to be the same as the one that tokio::main provides. Is that possible?


Solution

  • Ideally, I would like to set the default executor used by rt::spawn to be the same as the one that tokio::main provides. Is that possible?

    Not possible, tokio = "0.2.11" is able to spawn tasks only if they implement std::future::Future, you are not able to create these tasks with futures = "0.1.29", to do you need to use version 0.3.x or you need to use compat feature to convert old futures to new one (see OP's Q&A for details).

    This result in SpawnError { is_shutdown: true } panics.

    The Hyper that you are using uses tokio runtime internally, which is an older version of Tokio(it is also in your Cargo.lock with newer version). In Tokio it is not possible to spawn tasks without an initiated runtime, so you need to spawn your task like this :

    fn main() -> Result<(), std::io::Error> {
        rt::run(future_01());
        Ok(())
    }
    

    Warning:

    If you still want to use 2 different runtime, one from new and the other from old version, don't use it like below! You'll need to run them in seperate threads.

    //don't use this code!
    #[tokio::main]
    async fn main() -> Result<(), std::io::Error> {
        rt::run(future_01());
        Ok(())
    }
    

    Since main is an async and rt::run is a blocking function(why you shouldn't call long running code inside async fn )