Macro selectme::inline

source ·
macro_rules! inline {
    ($($tt:tt)*) => { ... };
}
Expand description

The inline! macro provides an inlined variant of the select! macro.

Instead of awaiting directly it evaluates to an instance of the Select or StaticSelect allowing for more efficient multiplexing and complex control flow.

When combined with the static; option it performs the least amount of magic possible to multiplex multiple asynchronous operations making it suitable for efficient and custom abstractions.


Branches and state

The inline! macro differs from select! in that it allows for retaining information on which branches have been disabled due to the future completing without having to rely on a [branch condition].

In contrast to select! it does not allow for performing additional asynchronous operations in the branches, nor affecting the control flow of the surrounding context like through break.

The following would not compile, since the break below is not evaluated in the context of the loop:

let s1 = std::future::ready(());

loop {
    selectme::inline! {
        () = s1 => 1,
        else => break,
    }.await;
}

While this seems like it would make it less useful, instead it means that it can efficiently perform inline common inline operations like timeouts.

use std::time::Duration;
use tokio::time;

async fn async_operation() -> u32 {
    // work here
}

let output = selectme::inline! {
    output = async_operation() => Some(output),
    () = time::sleep(Duration::from_secs(5)) => None,
}.await;

match output {
    Some(output) => {
        assert_eq!(output, 42);
    }
    None => {
        panic!("operation timed out!")
    }
}

Static selects

The inline! macro can also make use of the static; option, which allows the select that is generated to be named. This can be useful if you want to incorporate selection-like behaviour into another future.

Note that this option is not supported in select!.

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;

use pin_project::pin_project;
use selectme::{Random, StaticSelect};
use tokio::time::{self, Sleep};

#[pin_project]
struct MyFuture {
    #[pin]
    select: StaticSelect<u8, (Sleep, Sleep), Random, Option<u32>>,
}

impl Future for MyFuture {
    type Output = Option<u32>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.project();
        this.select.poll_next(cx)
    }
}

let s1 = time::sleep(Duration::from_millis(100));
let s2 = time::sleep(Duration::from_millis(200));

let my_future = MyFuture {
    select: selectme::inline! {
        static;

        () = s1 => Some(1),
        _ = s2 => Some(2),
        else => None,
    }
};

assert_eq!(my_future.await, Some(1));

Examples

use std::time::Duration;
use tokio::time;

let s1 = time::sleep(Duration::from_millis(100));
let s2 = time::sleep(Duration::from_millis(200));

let output = selectme::inline! {
    () = s1 => Some(1),
    _ = s2 => Some(2),
    else => None,
};

tokio::pin!(output);

let mut values = Vec::new();

while let Some(output) = output.as_mut().next().await {
    values.push(output);
}

assert_eq!(values, &[1, 2]);