Calling async Rust code from synchronous
Sometimes I find that I have some async
code that I really want to call from another project, but I don’t want async
/await
to infect my entire codebase. At least using tokio
, there’s an easy way to do this. Given some async project:
cargo new --lib my-async-crate
cargo add tokio
Which exposes an async
function:
pub async fn sleep_a_bit(num_seconds: u64) {
println!("Hold please...");
tokio::time::sleep(std::time::Duration::from_secs(num_seconds)).await;
println!("Thanks for waiting!");
}
We then have a project which wants to use our cool sleep_a_bit
function:
cargo new my-project
fn main() {
my_async_crate::sleep_a_bit(5);
}
This compiles, but it doesn’t do what we want:
warning: unused implementer of `Future` that must be used
--> src/main.rs:2:5
|
2 | my_async_crate::do_async_await(5);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: futures do nothing unless you `.await` or poll them
= note: `#[warn(unused_must_use)]` on by default
If we run this project, it will immediately exit. Thus, we need to include the .await
call:
fn main() {
// ---- this is not `async`
my_async_crate::sleep_a_bit(5).await;
// ^^^^^^ only allowed inside `async` functions and blocks
}
What we need is to create a tokio Runtime
that can synchronously block until the inner asynchronous operations complete. To do this, we add tokio with the rt
feature:
cargo add tokio --features rt
The main function then creates the runtime and creates a Future
. For this example, we’ll just block_on
:
fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
my_async_crate::sleep_a_bit(5).await;
println!("And we're back!");
});
}