Macro async_fn::before_async[][src]

macro_rules! before_async {
    (@) => { ... };
    ($($_ : tt) *) => { ... };
}
Expand description

Helper macro to express eagerly executed code (before the async suspension) when inside #[async_fn::bare_future]-annotated function.

Eager vs. lazy / suspended code?

Consider:

use ::async_fn::prelude::*;

/// Consider:
async fn lazy_print() -> i32 {
    println!("Hi");
    42
}
/// i.e.,
fn lazy_print_unsugared() -> impl Fut<'static, i32> {
    /* return */ async move { // <- lazy future / suspension point
        println!("Hi"); // <- this runs _after_ returning
        42
    }
}

/// vs.
fn eager_print() -> impl Fut<'static, i32> {
    println!("Hi"); // <- this runs _before_ returning
    /* return */ async move { // <- lazy future / suspension point
        42
    }
}

Both lazy_print().await and eager_print().await shall behave the same insofar that they’ll both print "Hi" and then resolve to 42.

That being said, when doing:

let future = mk_future();
println!("Bye");
assert_eq!(future.await, 42);

then, depending on whether mk_future refers to eager_print or lazy_print, the println!("Hi") statement will, respectively, be executed before returning the future or be part of the not-yet-polled future. That is, "Hi" will be printed before "Bye" or after.

While this may look like a contrived example, when future is spawned / to be polled within a parallel executor (e.g., mt_executor::spawn(mk_future()); println!("Bye");), then in the eager case we’ll have the "Hi" statement definitely occur before the "Bye" statement, whereas in the lazy case there will be no clear ordering between the two: "Hi" and "Bye" could appear in any order, or even intermingled!

Now, imagine if "Hi" were a dereference of a short-lived borrow, (such as let x = *borrowed_integer; or Arc::clone(borrowed_arc)), and if "Bye" were a statement dropping the borrowee. While in the eager case we’d have a clear happens-before relationship that’d guarantee soundness, in the lazy case we wouldn’t have it, and it would thus be very well possible to suffer from race conditions or a use-after-free! In Rust this means we’ll hit borrow-checker errors.

So this whole thing has to do with lifetimes:

  • either the returned future is short-lived; this is the case of:

    • async fn …(borrowed_arc: &Arc<…>) -> i32

    • or fn …_unsugared(borrowed_arc: &'_ Arc<…>) -> impl Fut<'_, i32>, )

    Such futures are thus incompatible with a long-lived spawn().

  • or the future is to be compatible with long-lived spawn()s (it must be 'static, and often, also Send); this is the case of:

    • fn …(borrowed_arc: &'_ Arc<…>) -> impl Fut<'static, i32>

    and for that to actually pass borrowck any dereference in the fn body (such as Arc::clone(borrowed_arc)) has to be done eagerly:

    fn some_future(borrowed_arc: &'_ Arc<Stuff>) -> impl Fut<'static, Ret> {
        let owned_arc = Arc::clone(borrowed_arc);
        async move /* owned_arc */ {
            stuff(&owned_arc).await
        }
    }

But in the case of a #[async_fn::bare_future] async fn, the whole function body is automagically wrapped within an async suspension!

That’s thus the purpose of this macro:

Usage

When inside the body of a #[async_fn::bare_future] async fn, this macro can be called as the very first statement of the function’s body, with statements inside it (any other usage is an error, or might error in future semver-compatible releases ⚠️).

That will make the statements be executed eagerly / before the async suspension (hence the name of the macro).

use ::async_fn::prelude::*;
use ::std::sync::Arc;

struct FooInner { /* … */ }
impl FooInner {
    async fn stuff(&self) -> Result<i32, Error> {
        /* … */
    }
}

pub struct Foo {
    inner: Arc<FooInner>,
}


impl Foo {
    #[async_fn::bare_future]
    pub async fn stuff(&self) -> impl Fut<'static, Result<(), Error>> {
        before_async! {
            let inner = Arc::clone(&self.inner);
        }
        let x = inner.stuff().await?; // <- await point.
        println!("{}", x);
        Ok(())
    }
}