-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Add iter
macro
#137725
base: master
Are you sure you want to change the base?
Add iter
macro
#137725
Conversation
e925043
to
b2afc00
Compare
This comment has been minimized.
This comment has been minimized.
b2afc00
to
abd590c
Compare
This comment has been minimized.
This comment has been minimized.
abd590c
to
180a69b
Compare
This comment has been minimized.
This comment has been minimized.
Make sure there is a check-pass test for when the coroutine-closure does capture things but those things don't force it to be lending. |
I took a look at the tests and they match the syntax I was expecting. |
fn plain() -> impl Fn() -> impl Iterator<Item = u32> { | ||
iter! { |
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.
Oh this is interesting. I was expecting this case would return impl IntoIterator
, not an impl GenFn
-equivalent. I don't necessarily mind it though.
I do want to check that we're confident that we can create a blanket impl for impl IntoIterator for GenFn {}
(or do something equivalent) so that the following snippet will just work once we add GenFn
proper:
let iter = iter! {
for x in 5..10 {
yield x * 2;
}
};
for x in iter { // <- calls `.into_iter` for you
println!("{x}");
}
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 also would expect this to return impl IntoIterator
. Maybe one day impl Iterator
, but we can defer that discussion for later.
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 thought the recent direction has been to prefer functions as the more powerful version of Into*
, so this seems in line with that.
That said, I think it's important that the output of the iter!
can go directly into a for
loop if there is no ||
.
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.
So I guess that's the surprising part here, that this example doesn't have ||
.
Okay, I take my comment back. I think the decision was to have two variants of it:
|
The latest resolution was the outcome of the 2025-02-12 meeting, where between @tmandry and I, we agreed to do:
In the first example, the The Iter closures return types that implement We agreed we'd later discuss possibly going further on the |
That's not how async closures work. We should do both at the same time, but it seems entirely orthogonal |
Here's how we got here, roughly: Alice: We should ship gen blocks somehow. Bob: Probably we want to support self borrowing though. Alice: But that needs a new trait. Bob: Yes. We do have a design for this that probably works. Alice: Still, it'd be nice if we could ship something before settling those details. Bob: Agreed, but we don't want to use the "nice" syntax for that. Alice: Yes, also we probably want these to be lending. Bob: Yes; that's even more to work out though, and the trait solver isn't really yet up for that. Alice: And I'd prefer if we didn't end up with a bunch of different bounds for all these, lending vs non-lending, self-borrowing vs not self-borrowing, etc. Bob: Yes; that may be hard to avoid though. OK, what if we ship a kind of placeholder for now? We won't expose the traits or the bounds, and we'll use a macro syntax. Alice: Yes, that could work. Maybe Bob: We probably don't quite want to do that. We know better now that people will immediately want to do Alice: Yes, we've already learned that lesson, I suppose there's no point repeating it and having to migrate people off of it. So what does that mean? Bob: Well, we'll need to put the bars in the macro invocation. Then we can control the expansion. Alice: So we'd have Bob: Actually, I'm not sure we really need the former. Another thing we've learned is that it might have been better if the "main" trait had been Alice: Yes, we recently talked about that during the async closures stabilization. We do prefer seeing the more general trait in bounds. Bob: Yes, and what we came away with is that, fortunately, this is kind of OK because thunk async closures are a "better" Alice: Right, so in that way, we kind of get another shot at this. We can push people toward Bob: Yes, and by passing around thunk async closures rather than futures, it avoids problems like the one that Yosh wrote about that kicked off that last-minute discussion about the async closures stabilization. Alice: Ah, I see. If the examples in his post used Bob: That's right. This kind of thing was also on our mind when we drafted the gen blocks RFC 3513. All the examples there are written as Alice: And since we now know that thunk iter closures are the "better" Bob: That's right. We could even probably blanket implement Alice: Yes, that makes sense. So what do we do about Bob: We just ship the iter closure form of it, Alice: That makes sense. Stepping back to how we're trying to avoid a migration, people still might write the pseudo iter closure bounds that won't really work, though, since we wouldn't be shipping the bounds, right? Bob: That's right, exactly as we saw with async. Probably I'd go ahead and just ship the Alice: Makes sense. OK, turning to syntax, maybe it'd be nice if there was a shorthand for when there are no arguments. We could use Bob: Requiring the bars there doesn't bother me that much, and it seems maybe a bit ad-hoc to lean on that coincidence about the return type, but we could try that. Maybe it's enough of a win to be worth that. As long as it returns a thunk iter closure, it's still aligned with this model. Sounds like we're pretty close here. Alice: One other thing. It might feel better to people to write Bob: Reasonable enough. We're using a placeholder syntax here anyway. It's probably fine to do this even without having done it for async yet. Let's get all that ready to go. |
Makes sense. Let's treat this PR as an MVP then and figure it out together with async closures |
I understand this as a future possibility, not the agreement we reached in the meeting. My understanding is that It doesn't make sense to me to make something callable that doesn't use closure syntax – unless we essentially deprecate IntoIterator in favor of closures, which should be a separate discussion. |
Interesting. Things were a bit scattered at the end of that meeting in trying to work out where we had agreement and didn't and whatnot. For my part, as described in the Alice and Bob story, and in how I similarly framed it in the meeting, due to how we're walking down the "thunk closures are the better Probably we're just confusing everyone at this point, though. Let's talk about it tomorrow. I think what we can say right now is we have agreement on how |
Move `yield` expressions behind their own feature gate In order to make progress with the `iter!` macro (e.g. in rust-lang#137725), we need `yield` expressions to be available without the `coroutines` feature. This PR moves `yield` to be guarded by the `yield_expr` feature so that we can stabilize that independently (or at least, concurrently with the `iter_macro` feature). Note that once `yield` is stable, it will still be an error to use `yield` expressions outside something like a generator or coroutine, and these features remain unstable. r? `@oli-obk`
☔ The latest upstream changes (presumably #138114) made this pull request unmergeable. Please resolve the merge conflicts. |
Rollup merge of rust-lang#138081 - eholk:yield-feature, r=oli-obk Move `yield` expressions behind their own feature gate In order to make progress with the `iter!` macro (e.g. in rust-lang#137725), we need `yield` expressions to be available without the `coroutines` feature. This PR moves `yield` to be guarded by the `yield_expr` feature so that we can stabilize that independently (or at least, concurrently with the `iter_macro` feature). Note that once `yield` is stable, it will still be an error to use `yield` expressions outside something like a generator or coroutine, and these features remain unstable. r? `@oli-obk`
180a69b
to
0e8d7ea
Compare
This comment has been minimized.
This comment has been minimized.
0e8d7ea
to
51c75e7
Compare
let lo = parser.token.span.shrink_to_lo(); | ||
let block = parser.parse_block_tail( | ||
lo, | ||
ast::BlockCheckMode::Default, | ||
rustc_parse::parser::AttemptLocalParseRecovery::No, | ||
)?; | ||
let fn_decl = cx.fn_decl(Default::default(), ast::FnRetTy::Default(span)); | ||
let closure = ast::Closure { | ||
binder: ClosureBinder::NotPresent, | ||
capture_clause: CaptureBy::Ref, | ||
constness: Const::No, | ||
coroutine_kind, | ||
movability: ast::Movability::Movable, | ||
fn_decl, | ||
body: cx.expr_block(block), | ||
fn_decl_span: span, | ||
fn_arg_span: span, | ||
}; | ||
if parser.token != token::Eof { | ||
parser.unexpected()?; | ||
} | ||
let span = lo.to(parser.token.span); | ||
Ok(cx.expr(span, ExprKind::Closure(Box::new(closure)))) |
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.
this part can be ripped out now
See related discussion in https://rust-lang.zulipchat.com/#narrow/channel/481571-t-lang.2Fgen/topic/iter!.20macro/near/500784563
very little error case testing so far, but the success path works.
There is also no
IterFn
trait yet, as T-lang didn't consider it something urgently needed I think we can implement it in follow-up PRs.r? lang for the tests, @compiler-errors for the impl