tokio-fix

Build License Cargo Documentation

Unlock yourself from the tokio walled garden.

[Tokio] uses [non-standard] I/O traits (causes compilation errors) and has the concept of "tokio context" (causes runtime errors). Using [tokio]-based crates outside the [tokio] runtime can be a frustrating experience, thus locking you into the [tokio] ecosystem and preventing you from using other async crates.

This crate is a magic wand that fixes such compilation and runtime errors caused by [tokio].

Example

Here's a classic "cat" example that reads lines from stdin and echoes them into stdout. We'll use [tokio]'s stdin/stdout types, but rely on [futures] for everything else:

```rust fn main() -> std::io::Result<()> { futures::executor::block_on(async { let stdin = tokio::io::stdin(); let mut stdout = tokio::io::stdout();

    futures::io::copy(stdin, &mut stdout).await?;
    Ok(())
})

} ```

Fix compilation errors

We get errors because [tokio::io::AsyncRead] and [tokio::io::AsyncWrite] are different from [futures_io::AsyncRead] and [futures_io::AsyncWrite]:

error[E0277]: the trait bound `tokio::io::Stdin: futures::AsyncRead` is not satisfied --> example.rs:6:9 | 6 | futures::io::copy(stdin, &mut stdout).await?; | ^^^^^^^^^^^^^^^^^ the trait `futures::AsyncRead` is not implemented for `tokio::io::Stdin`

error[E0277]: the trait bound `tokio::io::Stdout: futures::AsyncWrite` is not satisfied --> example.rs:6:34 | 6 | futures::io::copy(stdin, &mut stdout).await?; | ^^^^^^^^^^^ the trait `futures::AsyncWrite` is not implemented for `tokio::io::Stdout`

Let's apply .fix() and .fix_mut() to convert between [tokio]-based and [futures]-based types:

```rust use tokio_fix::Fix;

fn main() -> std::io::Result<()> { futures::executor::block_on(async { let stdin = tokio::io::stdin(); let mut stdout = tokio::io::stdout();

    futures::io::copy(stdin.fix(), &mut stdout.fix_mut()).await?;
    Ok(())
})

} ```

Fix runtime errors

Now the program compiles, but we get an error at runtime:

thread 'main' panicked at 'not currently running on the Tokio runtime.' note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Yikes! This is because [tokio]'s stdin and stdout are used outside the [tokio] context. We can apply .fix() on the [futures::io::copy()] future to let it enter the [tokio] context:

```rust use tokio_fix::Fix;

fn main() -> std::io::Result<()> { futures::executor::blockon(async { let stdin = tokio::io::stdin(); let mut stdout = tokio::io::stdout(); futures::io::copy(stdin.fix(), &mut stdout.fixmut()).fix().await?; Ok(()) }) } ```

Problem solved. It is also possible to apply fix() to the outer future passed to [futures::executor::block_on()]:

```rust use tokio_fix::{fix, Fix};

fn main() -> std::io::Result<()> { futures::executor::block_on(fix(async { let stdin = tokio::io::stdin(); let mut stdout = tokio::io::stdout();

    futures::io::copy(stdin.fix(), &mut stdout.fix_mut()).await?;
    Ok(())
}))

} ```

Fix in any direction

In the previous example, we were using [tokio] from [futures], but it is also possible to use [futures] from [tokio]. All the fixes are applied very similarly:

```rust use blocking::Unblock; use tokio_fix::Fix;

[tokio::main]

async fn main() -> std::io::Result<()> { let mut stdin = Unblock::new(std::io::stdin()); let mut stdout = Unblock::new(std::io::stdout());

tokio::io::copy(&mut stdin.fix_mut(), &mut stdout.fix_mut()).await?;
Ok(())

} ```

Here, we're using the [blocking] crate, which is based on [futures] rather than [tokio].

Use tokio-based crates

Now you can use any [tokio]-based crate without being locked into the [tokio] ecosystem!

Let's use [reqwest] and [warp] from [futures]:

```rust use tokio_fix::fix; use warp::Filter;

fn main() { futures::executor::block_on(fix(async { // Make an HTTP GET request. let response = reqwest::get("https://www.rust-lang.org").await.unwrap(); println!("{}", response.text().await.unwrap());

    // Start an HTTP server
    let routes = warp::any().map(|| "Hello from warp!");
    warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
}))

} ```

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.