Skip to content

Experiment with a proc-macro-free API #71

Open
@SabrinaJewson

Description

@SabrinaJewson

I've been thinking that it would be possible for this crate to provide an API that doesn't use proc macros at all, which has a couple of benefits:

The API could look like this:

/// async-stream ///

pub fn stream<T, F, Fut>(f: F) -> impl Stream<Item = T>
where
    F: FnOnce(Yielder<T>) -> Fut,
    Fut: Future<Output = ()>,
{ /* ... */ }

pub fn try_stream<T, E, F, Fut>(f: F) -> impl Stream<Item = Result<T, E>>
where
    F: FnOnce(Yielder<T>) -> Fut,
    Fut: Future<Output = Result<(), E>>,
{ /* ... */ }

// This macro will shadow the yielder with a function that borrows from a local.
//
// It will panic if called after the first poll or from a different stream.
#[macro_export]
macro_rules! start_stream {
    // $yielder must be of type Yielder<T>
    ($yielder:ident) => { /* ... */ };
}

/// Usage ///

let stream = async_stream::stream(|yielder| async move {
    // Must be called in the first poll, otherwise the stream will panic
    start_stream!(yielder);
    yielder(1).await;
    yielder(2).await;
    yielder(3).await;
});

I'm pretty sure this would be sound. Ergonomically, we'd lose the nice for await and yield syntax as well as the ability to use ? in regular streams (although users can always use a try_stream and then flatten the results if they want something like that), but we'd also gain the ability to specify the type of stream with turbofish syntax. I think it might be nice to support both versions in the library, depending on users' preferences. Any thoughts on the design?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions