17

I have a function to download a file which is async:

async fn download_file() {
    fn main() {
        let resp = reqwest::blocking::get("https://sh.rustup.rs").expect("request failed");
        let body = resp.text().expect("body invalid");
        let mut out = File::create("rustup-init.sh").expect("failed to create file");
        io::copy(&mut body.as_bytes(), &mut out).expect("failed to copy content");
    }
}

I want to call this function to download a file and then await it when I need it.
But the problem is if I do it like this, I get an error:

fn main() {
    let download = download_file();
    // Do some work
    download.await; // `await` is only allowed inside `async` functions and blocks\nonly allowed inside `async` functions and blocks
    // Do some work
}

So I have to make the main function async, but when I do that I get another error:

async fn main() { // `main` function is not allowed to be `async`\n`main` function is not allowed to be `async`"
    let download = download_file();
    // Do some work
    download.await;
    // Do some work
}

So how can I use async and await
Thanks for helping

msrd0
  • 7,816
  • 9
  • 47
  • 82
NightmareXD
  • 425
  • 1
  • 4
  • 14
  • Related: [Do you always need an async fn main() if using async in Rust?](https://stackoverflow.com/questions/62309065/do-you-always-need-an-async-fn-main-if-using-async-in-rust) – Chayim Friedman Feb 14 '22 at 19:51

5 Answers5

19

The most commonly used runtime, tokio, provides an attribute macro so that you can make your main function async:

#[tokio::main]
async fn main() {
    let download = download_file().await;
}

In case you don't have a dependency on tokio just yet, add the following to your Cargo.toml:

tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
msrd0
  • 7,816
  • 9
  • 47
  • 82
  • as of 10/18/22, Cargo.toml statement for tokio is `tokio = { version = "1.21.2", features = ["full"] }`, not `tokio = "1.21.2"` – John Miller Oct 19 '22 at 03:28
  • 4
    @JohnMiller you don't need `full`, just runtime and macro features are enough – msrd0 Oct 19 '22 at 12:33
8

An established pattern for this is here.

From a synchronous block, build a new runtime then call block_on() on it:

let rt = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap();

let res = rt.block_on(async { something_async(&args).await });
t56k
  • 6,769
  • 9
  • 52
  • 115
3

you can not make run as async because there must be a parent thread which is going to poll the result of main function. if main is itself is async then who is going to poll main? the return type of a async function is always something like this, impl future<output= Sometype + sometrait(if exist)> . the future tarit also provide the poll method so that the executor may know when to proceed . See rust asyn prog. in rust to understand more in dept, you can call block_on(some async task) in main method, which will block the current thread ie main until the given async task is not finished, on the other hand , await does not block the current thread. which is why a function which use .await must be async at first place

  • 1
    While this is true, it doesn't answer OP's (simple) question of just wanting to know how to actually run their async code, and @msrd0's answer shows the simpest usecase >95% of async Rust users will be happy with. – leo848 Oct 15 '22 at 20:59
3

You can also use async-std as your async implementation.

Your Cargo.toml should have this:

[dependencies]
async-std = {version = "1.12.0", features = ["attributes"]}

And then you can use it like this:

#[async_std::main]
async fn main() {
 ....
}
Radu Diță
  • 13,476
  • 2
  • 30
  • 34
1

Run async function into non async with tokio::runtime:

use tokio::runtime::Runtime;

async fn async_bar() -> String {
    println!("run async bar");
    String::from("return async bar")
}

fn non_async_foo() {
    let rt = Runtime::new().unwrap();
    
    let future = async_bar();
    let r = rt.block_on(future);
    
    println!("{r}");
}

fn main() {
    non_async_foo();
}

This example working for me with tokio features = ["full"]

Or better way use futures::executor see https://stackoverflow.com/a/74624227/8111346

kolserdav
  • 260
  • 4
  • 14