#future #multiplex #simultaneously #async #compilation #run-time

async-select

select! multiplex asynchronous futures simultaneously

3 unstable releases

0.2.0 May 10, 2024
0.1.1 May 10, 2024
0.1.0 May 9, 2024

#272 in Asynchronous

Download history 6/week @ 2024-02-18 378/week @ 2024-05-05 41/week @ 2024-05-12 58/week @ 2024-05-19

477 downloads per month
Used in asyncs

Apache-2.0

150KB
4K SLoC

async-select

crates.io github-ci docs.rs Apache-2.0

select! multiplex asynchronous futures simultaneously.

Motivations

Initially, I opened an issue Support multiple async runtimes but not only tokio for zookeeper-client-rust. I saw, there are other projects in community to assist runtime agnostic libraries. Though, I wouldn't say possibly most of they if not all are horrible -:), but I wouldn't they are elegant either. After created spawns, I found that it is actually extremely easy to migrate from tokio runtime for library authors with help from runtime agnostic libraries, say async-io, async-net, futures, futures-rustls, futures-lite and etc.. But there are still tokio dependencies in tree, one is select!, I want to give it a try.

Features

use core::future::ready;

use async_select::select;

#[derive(Default)]
struct FieldStruct {
    _a: TupleStruct,
    _b: i32,
    _c: i32,
}

#[derive(Default)]
struct TupleStruct((), (), ());

// pattern according to syn::Pat
//
// failure will cause compilation error.
async fn patterns() {
    select! {
        default => {},
        complete => {},

        // Const(PatConst)
        //
        // unstable: #![feature(inline_const_pat)] https://github.com/rust-lang/rust/issues/76001
        // const { 5 } = ready(5) => {},

        // Ident(PatIdent)
        mut _v = ready(()) => {},
        ref _v = ready(()) => {},
        ref mut _v = ready(()) => {},
        ref mut _x@FieldStruct{ _b, ..} = ready(FieldStruct::default()) => {},
        ref mut _x@FieldStruct{ mut _b, ..} = ready(FieldStruct::default()) => {},
        mut _x@FieldStruct{ mut _b, ..} = ready(FieldStruct::default()) => {},


        // Lit(PatLit)
        5 = ready(5) => {},

        // Macro(PatMacro)

        // Or(PatOr)
        5 | 6 = ready(5) => {},

        // Paren(PatParen)
        (5 | 6) = ready(5) => {},

        // Path(PatPath)
        ::core::option::Option::None = ready(Some(5)) => {},
        ::core::option::Option::Some(ref _i) = ready(Some(5)) => {},

        // Range(PatRange)
        1..=2 = ready(5) => {},

        // Reference(PatReference)
        //
        // This is not supported as we are pattern against value.
        // &_v = ready(5) => {}
        // &mut _v = ready(5) => {}

        // Rest(PatRest)
        (ref _i, mut _v, ..) = ready((1, 2, 3, 4)) => {},

        // Slice(PatSlice)
        //
        // Pattern against value but not reference.

        // Struct(PatStruct)
        FieldStruct { ref mut _a, ref _b, .. } = ready(FieldStruct::default()) => {},

        // Tuple(PatTuple)
        (1, 2) = ready((1, 2)) => {},

        // TupleStruct(PatTupleStruct)
        TupleStruct(_a, _b, ..) = ready(TupleStruct::default()) => {},
        TupleStruct(ref mut _a, ref _b, ..) = ready(TupleStruct::default()) => {},

        // Type(PatType)
        // Is this only used in variable definition ?

        // Verbatim(TokenStream)
        //
        // Tokens in pattern position not interpreted by Syn.

        // Wild(PatWild)
        _ = ready(()) => {}
    }
}
  • crossbeam-channel: this is where I learned what real marcos look like.
  • stuck::select: this is where async-select::select! derive from.
  • futures::select: this is where default and complete derive from.
  • tokio: tokio is great on it own. But it is apprently not kind to runtime agnostic library. You simply can't tell which part of it is runtime agnostic. So better to avoid it entirely if you even want runtime agnostic.
  • The Little Book of Rust Macros: hmm, the book.

No runtime deps