Indeed, in fact that’s how they’re implemented (modulo the details of ‘not ready’, as @Nemo157 has pointed out) . You can see this in lines 432 488 of the source linked in the OP.
VoidStream = Generator<Yield=(), Return=Void>
UnitStream<C> = Generator<Yield=(), Return=C>
InfiniteStream<Y> = Generator<Yield=Option<Y>, Return=Void>
FiniteStream<Y, C> = Generator<Yield=Option<Y>, Return=C>
Note that I use () rather than Async<Void> and Option<Y> rather than Async<Y>, but these are basically equivalent.
What I did not know until I did some more research (prompted by your comment) is that the never_type feature is actually a thing. As you probably already know, it even allows you to have non-exhaustive patterns where the patterns involving ! can be omitted. For example:
#![feature(never_type)]
fn main() {
let x: Option<!> = None;
match x {
None => println!("hello"),
}
}
I remember testing this with my Void type and being disappointed that the compiler did not allow me to omit the cases with Void.
This is a bit of a game changer for me, and reduces the need for the different types significantly. I’m going to play around with it and see how it goes.
=== UPDATE ===
So I’m just about done refactoring due to the introduction of never_type. The result is so much cleaner! Deleting large sections of the code was very satisfying. Turns out you do only need to distinguish between future and stream to keep the compiler happy, the other types ‘just work’ without having to specify them. I’m also really glad to have only 1 Poll type and only 1 Stream trait now.
The only addition is that there are now 3 variations of await (before there were only 2):
await_item!(Stream<Y, !>) -> Y
await_complete!(Stream<Y, C>) -> C
await_either!(Stream<Y, C>) -> Result<Y, C>
I think using Result here is actually not a good choice. But I’m out of time to work on this for now. Will look at it again later on.
https://play.rust-lang.org/?gist=6ff453884fa4357e857dab1ee03bf38f&version=nightly
Interesting Idea #3
The futures-await readme suggests that async functions cannot take references as parameters due to borrowing rules. Yet, with my implementation this seems to compile and run as expected. Maybe I’m missing the point? Maybe this is unsafe?
fn foo<'a>(s: &'a str) -> impl Stream<!, &'a str> + 'a {
future!({
not_ready!();
println!("{}", s);
not_ready!();
s
})
}
let some_str = String::from(">>> testing 123 <<<");
let bar = foo(&some_str);
poll_stream_to_completion(bar);