Lazy async operations in Rust
Tags: rust, async
Published: 2021-07-21 07:52:08

## async and await

JavaScript developers may feel at home with Rust's concurrency model, which uses the `async`/`await` concept. In JavaScript we have [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) to represent eventual results of async operations, and in Rust we have [`Future`](https://doc.rust-lang.org/std/future/trait.Future.html).

The following shows an example of `async` and `await` syntax in JavaScript and Rust code:

**JavaScript**

```javascript
// greet.js

async function main() {
  await greet("Hi there!");
}

async function greet(message) {
  console.log(message);
}

/*
Output:

Hi there!
*/
```

**Rust**

```rust
// greet.rs

#[tokio::main]
async fn main() {
    greet("Hi there!").await;
}

async fn greet(message: &str) {
    println!("{}", message);
}

/*
Output:

Hi there!
*/
```

Currently, Rust does not include an async runtime out-of-the-box. We use the [Tokio runtime](https://tokio.rs/) in our examples.

<!--truncate-->

## Executing async operations without suspending the running function

Now, consider a scenario where we want to perform an async side-effect, such as dispatching a message, but we do not want to suspend the running function until this side-effect completes.

In JavaScript we can call the `publish` function, which returns a Promise, without needed to `await` it. `setTimeout` queues the given callback on the event loop and immediately returns a `Promise`. The `main` function then logs "Don't block me!" to the console. Finally the event loop eventually runs our callback, which logs "Executed main()".

```javascript
// publish.js

async function main() {
  publish("Executed main()"); // no await
  console.log("Don't block me!");
}

// returns a Promise
function publish(message) {
  return setTimeout(() => {
    console.log(message);
  }, 0);
}

/*
Output:

Don't block me!
Executed main()
*/
```

Things look different in Rust when we do not `await` the `Future` returned by `publish`.

```rust
// publish.rs

#[tokio::main]
#[allow(unused_must_use)]
async fn main() {
    publish("Executed main()");  // no await
    println!("Don't block me!");
}

async fn publish(message: &str) {
    println!("{}", message);
}

/*
Output:

Don't block me!
*/
```

For some reason, the Rust program output does not show "Executed main()"! According to the Rust [documentation on `Future`](https://doc.rust-lang.org/std/future/trait.Future.html), "futures do nothing unless you `.await` them". Unlike JavaScript, Rust's async operations are **lazy**.

In Rust we must use `.await` on the `Future` to execute the async operation. However awaiting the `publish` causes the `main` function to suspend and prevent execution of `println!("Don't block me!")` until after `publish` completes. To avoid this situation and achieve a similar behaviour to JavaScript's Promise handling, we can use `tokio::spawn`.

```rust
// publish.rs

#[tokio::main]
async fn main() {
    // Spawns a new async task, and executes it concurrently
    tokio::spawn(async {
        publish("Executed main()").await;
    });

    println!("Don't block me!");
}

async fn publish(message: &str) {
    println!("{}", message);
}

/*
Output:

Don't block me!
Executed main()
*/
```

It works as we intended! `tokio::spawn` executes `publish` concurrently without blocking the remaining lines operations in `main`!