kota's memex

Rust supports many different models of concurrency including the "async" model popularized by the node ecosystem. It allows us to express how operations could be asynchronous in terms of potential pausing points and eventual results.

use trpl::Html;

fn main() {
    let args: Vec<String> = std::env::args().collect();

    trpl::block_on(async {
        let url = &args[1];
        match page_title(url).await {
            Some(title) => println!("{url}: {title}"),
            None => println!("{url}: no title found"),
        }
    })
}

async fn page_title(url: &str) -> Option<String> {
    let resp = trpl::get(url).await;
    let resp_text = resp.text().await;
    Html::parse(&resp_text)
        .select_first("title")
        .map(|title| title.inner_html())
}

Say for example we need to download many files. The simple approach to avoid blocking our main thread by spawning a dedicated thread to download each file. However, the overhead of the system resources used by those threads could eventually become a problem. We can instead use rust's async features to define a number of tasks and allow the runtime to choose the best order and manner in which to run them.

futures and async syntax

A future is a value that may not be ready now, but will become ready at some point in the future. In rust futures are types that implement the Future trait.

You can apply the aync keyword to a function or block to specify that they can be interrupted and resumed. Within an async block, you can use the await keyword to await a future. This is a potential spot for the program's execution to pause and periodically check if the future is resolved.

async main?

Most languages that support async bundle a runtime, but Rust does not. Instead, there are many different async runtimes available, each of which makes different tradeoffs suitable to the use case it targets.

As a result main can’t be simply marked async. The code needs a runtime: a Rust crate that manages the details of executing asynchronous code. This is usually done by creating a block in your main function and calling a function from your runtime like tokio::block_on.

Some runtimes will include a macro allowing you to write async main which the macro will then transform into a normal main function with a block that calls the runtime.