-
Notifications
You must be signed in to change notification settings - Fork 1.6k
RFC: Add iter!
macro
#3861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
RFC: Add iter!
macro
#3861
Conversation
so |
This looks amazing! This does look like a nice incremental step forward. Has any thought been given to how this might be used in library APIs? One could of course return an |
|
Can you go into more detail on why this couldn't be a library? |
My biggest concern is that this is supposed to be about a macro for iterator creation, but it can use It's mildly confusing, and not what I expected from the RFC title, or what I'd usually expect from finding EDIT: also, personally I've never had a problem making custom iterators once I learned about using |
To me, this is very motivating -- |
I could think of a few future things that could help. For example, we could add something like That doesn't help with the fundamental problem of wanting to name a concrete type though, or implement other traits. A while back I was musing some about anonymous impls. I could see riffing on that with something like: iter!(|| { yield 1; yield 2; yield 3; } with fn size_hint(&self) { (3, Some(3)) }); or maybe iter!(|| { yield 1; yield 2; yield 3; } with impl DoubleEndedIterator {
fn next_back(&self) -> Option<Self::Item> { ... }
}); So I think there are possibilities, but it's definitely future work. Even being able to write one-off iterators inline seems like an improvement though! |
I definitely think that the inability to pin these iterators and use them for references is more limiting than helpful. Perhaps it's worth investigating whether something like #3851 could be done to allow a supertrait of |
You might be able to work around the concrete type issue with TAIT. type MapIter = impl Iterator;
struct Map(MapIter);
fn map<I: Iterator, T, F: Fn(I::Item) -> T>(iter: I, f: F) -> Map {
Map(iter!(|| {
for i in iter {
yield f(i);
}
})())
} I don't think this example will work exactly, since |
This RFC also includes stabilizing
In my mind I've kind of been saving the generator name for the more powerful version that allows self-borrows and borrowing across yield. The
I think there generators or fn concat(mut a: impl Iterator<Item = i32>, mut b: impl Iterator<Item = i32>) -> impl Iterator<Item = i32> {
let mut first_done = false;
core::iter::from_fn(move || {
if !first_done {
match a.next() {
Some(i) => return Some(i),
None => {
first_done = true;
return b.next();
}
}
}
b.next()
})
} On the other hand, with fn concat(a: impl Iterator<Item = i32>, b: impl Iterator<Item = i32>) -> impl Iterator<Item = i32> {
iter!(move || {
for i in a {
yield i;
}
for i in b {
yield i;
}
})()
} I think there's room for both approaches. If |
Well if this is a macro that expanded to a new "iterator closure" type category (which I've never really heard of as being its own "thing" before, and which the RFC does not particularly define despite having a section header about it), then why not call the macro
Yes, using Why can't the |
## Pre- and Post-fix Yield | ||
|
||
This RFC is written to allow `yield` in either a prefix position (`yield foo`) or a postfix position (`foo.yield`). | ||
For simple iterators that users are likely to write with `iter!`, the prefix form is often most familiar. | ||
However, in Rust, postfix operators such as `?` or `.await` are also common and natural. | ||
|
||
If we were to support full coroutines (see [Future Work][full-coroutines]), `yield` would be able to return a non-`()` value. | ||
In this case, there will likely be cases where it is convenient to write `foo().yield.do_something()` instead of `(yield foo()).do_something()`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you note, Rust generally uses postfix operators only in situations where chaining is natural. You mention the potential case of full coroutines which might benefit from that. But iter!
as specified in this RFC is narrowly tailored toward iterators, and doesn’t aim to have anything to do with full coroutines.
So, AFAICT, there is no reason to support postfix yield
here; all it does is break the “there should be one—and preferably only one—obvious way to do it” rule, and introduce confusion. We can always reevaluate when we do get full coroutines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One alternative would be to avoid committing to any syntax, and instead offer some sort of yield!()
macro (name would likely have to be different).
Expr.yield | ||
|
||
Yield expressions can be written as either `yield foo` or `foo.yield`. | ||
A `yield` with no arguments is equivalent to both `yield ()` and `().yield`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think this should be supported at all. An iterator that yields ()
is unusual enough that it should be indicated explicitly. And we can always add it later.
Perhaps |
There's a rather annoying issue with coroutines where taking a shared reference to a |
@Jules-Bertholet That's just one example. It doesn't really address the grander point. There are other traits you might want to implement too for an iterator exported in a library API. Notably |
And it did not intend to. |
One of the things the macro does is it creates a context where |
This is one of the reasons |
Tracking issue: rust-lang/rust#142269
Summary
Add an
iter!
macro to provide a better way to create iterators.Implementing the
Iterator
trait directly can be tedious. Generators (see RFC 3513) are available on nightly but there are enough open design questions that generators are unlikely to be stabilized in the foreseeable future.On the other hand, we have an
iter!
macro available on nightly that provides a subset of the full generator functionality. Stabilizing this version now would have several benefits:gen { ... }
andgen || { ... }
syntax for a more complete feature in the future.Rendered