Generators in rust

Is there a way to add generators like python to rust. A generator that can yield multiple results. This can help saving time and space while creating large objects.

There’s already a “generators” feature in development, see: generators - The Rust Unstable Book

That feature is for asynchronous objects/coroutines that can yield a sequence of values, as well as for ergonomically writing such things; for the more straightforward use-case of simply “saving space while creating large objects”, maybe you’re looking for Iterators? There’s also third-party crates offering the Stream abstraction as well as ergonomic macros such as async_stream::stream!.

2 Likes

There’s also the genawaiter crate if you prefer that.

1 Like

If hasn't been maintained in two years, issues are not resolved anymore and PR not merged either. It has several outdated dependencies so I don't think it's a very good idea to use it in its current state :confused:

GitHub - estebank/iterator_item: A syntax exploration of eventually stable Rust Iterator items is somewhat unmaintained at the moment, but it is meant more as an exploration on what the surface syntax should be, lifting some ideas from GitHub - withoutboats/propane: generators, which has also not received significant updates. But, I would love to merge any fixes, improvements for iterator_item, if people start using it in earnest.

2 Likes

Hello everyone !

Sorry to encroach on this topic with a noob question, but I have a hard time understanding the difference between iterators, generators and streams (both in the context of Rust and of programming in general). Could someone explain it to me ?

Would it be correct to assume that generators are simply the async equivalent of iterators ? or are there some more fundamental differences ?

There’s multiple approaches for how to create an “async equivalent of iterators”. A trait like futures::Stream is one of them. Another one would be to try to create a trait with an async fn next(&mut self) -> Option<Self::Item> method (note that support for async fn in traits is still in development), which would translate to a method returning a future producing a single item (and borrowing from the asyncronous iterator) whereas with Stream, all the state for polling until each item is produced must be part of the Stream value itself.

Comparing futures::Stream to std::ops::Generator, the two are fairly similar, the latter is more general in some ways though: Generator could be interpreted as a generalization of Future, Stream, and Iterator (depending on how R, Yield and Return are instantiated). Notably, Generator allows an arbitrary type in place of the &mut Context<'_> argument for Future or Stream, and it allows both yielding values on every poll (similar to Stream) and producing a final result upon completion (like a Future). Unlike Stream, a value is yielded on every poll, but Streams can be modeled Generators nonetheless by using Yield = Option<Item> (where None would not stand for end-of-stream, but instead stand for “the is no new item yet”, similar to Poll::Pending).

The reason why Generator is super general is because it’s main intention is to provide the interface for generator syntax, a generalization of async syntax (and AFAIR currently async is implemented by translating into a generator, together with a wrapper that makes a future out of it). This syntax should naturally be able to be used as flexible as possible without unnecessary additional restrictions. (Note that, in case that isn’t clear, both the Generator trait and generator syntax are still unstable, and possibly subject to future changes.)

2 Likes

I must admit some of it went over my head :sweat_smile:, but I think I got the gist of it.

Though now that you are talking about it, there there are a couple more questions that have popped up in my head:

  1. what's the motivation for generator syntax since async syntax already exists (wouldn't generator syntax clash with or supersede async syntax) ? and
  2. what would it bring to the table that manually implementing the Generator trait can't ? (which is also a question I have in regards to async syntax: is the Future trait so impractical to implement that we need a special syntax to automagically do it for us ?)

Having generator syntax allows experimenting with things like iterators defined via generators in user code (originally async syntax was implemented as a proc macro outputting generator syntax in a user library, before it was integrated into the compiler). The generator syntax itself is unlikely to be stabilised.

Manually dealing with lifetimes across yield points requires unsafe code, the generator (and by extension async) syntax makes this safe.

Source?

My understanding was that the current syntax is experimental and therefore isn't final and could be tweaked based on experience with it. But that there is a desire to eventually stabilise some version of it. Has something changed in this regard?

1 Like

There has been no RFC for generators themselves, only an eRFC to have them as a building block for async. Maybe someday there’ll be an RFC to actually have them as a public feature, but I’ve not seen any of the lang team really pushing for it. I would expect there to be an RFC about using them to add some sort of iterator syntax first.

The closest to an RFC for stabilizable semicoroutines are these accepted design notes:

https://lang-team.rust-lang.org/design_notes/general_coroutines.html

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.