Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upA `yield` construct in the vein of C# #7746
Comments
This comment has been minimized.
This comment has been minimized.
Sod-Almighty
commented
Jul 12, 2013
|
Seconded. Hopefully not too far in the future. |
This comment has been minimized.
This comment has been minimized.
|
@Sod-Almighty far-future milestone just means that it would not be a priority for the primary developers until after 1.0. It leaves open the possibility that a volunteer could implement it themselves, and such a patch could be accepted regardless of the current milestone. |
This comment has been minimized.
This comment has been minimized.
|
This could probably not be done with a syntax extension, because it needs to do some serious rewriting, typechecking, inference etc. Using some mockup syntax you'd have:
Which would get rewritten into something like this:
|
This comment has been minimized.
This comment has been minimized.
flying-sheep
commented
Jul 12, 2013
|
It's pretty much the only option we have to make our new external iterators as nice as the internal ones can be in situations where external ones usually suck. |
thestinger
referenced this issue
Jul 12, 2013
Closed
add a way to compile an internal iterator to an external one (state machine) like C# #5633
This comment has been minimized.
This comment has been minimized.
|
This would be really nice to have, because manually hoisting out the state into a Context switches for each iteration aren't acceptable so the solutions used by Ruby ( |
This comment has been minimized.
This comment has been minimized.
flying-sheep
commented
Jul 13, 2013
|
Also yield is very awesome for lexers: yield some tokens, delegate to a subparser, and yield more is trivial to do and leads to very elegant code. |
This comment has been minimized.
This comment has been minimized.
|
Interesting. I'd recommend digging up a formal description of the rewriting algorithm, and doing a patent search to ensure it's safe to prototype. Happy to reserve the keyword though |
This comment has been minimized.
This comment has been minimized.
|
Given that #5633 was assigned to Far-Future and was closed in favor of this bug, I'm assigning that milestone here. |
This comment has been minimized.
This comment has been minimized.
JulianBirch
commented
Aug 6, 2013
|
It would probably be desirable to support await as well, maybe clojure async style. I believe the state machine generation is fairly similar between the two cases. |
This comment has been minimized.
This comment has been minimized.
|
@bstrie: coincidentally enough, @huonw and I have been chatting about if we could support a syntax extension that can build state machines which compile down into computed gotos. This would be really handy for my ragel parser generator fork, and would probably help make yields as fast as possible. So if this issue goes forward, please try to leave some hooks available for general purpose state machines. |
This was referenced Sep 12, 2013
This comment has been minimized.
This comment has been minimized.
|
I thought a bit about this today. One problem with providing yield fn zero2ten() -> struct Zero2TenIter {
let mut x = 0;
while x <= 10 {
yield x;
x += 1;
}
}... fn zero2ten() -> Zero2TenIter { Zero2TenIter { start: 0, end: 10 } }
struct Zero2TenIter { start: uint, end: uint }
impl Iterator<uint> for Zero2TenIter {
fn next(&mut self) -> Option<uint> {
if self.start <= self.end {
let v = self.start;
self.start += 1;
Some(v)
} else {
None
}
}
}
impl DoubleEndedIterator<uint> for Zero2TenIter {
fn next_back(&mut self) -> Option<uint> {
if self.start <= self.end {
let v = self.end;
self.end -= 1;
Some(v)
} else {
None
}
}
}As you can see, the problem with that is that now upgrading a existing So, as a solution, how about just making the yield fn zero2ten() -> struct Zero2TenIter {
let mut start = 0;
let mut end = 10;
} front {
while start <= end {
yield start;
start += 1;
}
} back {
while start <= end {
yield end;
end -= 1;
}
}The idea here is to give the user three code blocks to fill out: initialization, which contains all the setup code for the iterator and any variable declarations that are shared state for both Iterator impls, and two code blocks corresponding to the two iterator impls. A user of this syntax would still have to take care of checking that both code blocks play nice together and properly recognize that the ranges meet in the middle, but they could do so more easily than in the manual implementation way. |
This comment has been minimized.
This comment has been minimized.
Sod-Almighty
commented
Nov 24, 2013
|
@Kimundi: Sounds good, but should be optional. Original syntax for single-ended iterators. |
This comment has been minimized.
This comment has been minimized.
JulianBirch
commented
Nov 24, 2013
|
The problem with that design is that it kind of defeats the object of the generator: to be able to fully use the normal control flow to generate the iterator, you're back to implementing
This allows for fairly arbitrary control flows. |
This comment has been minimized.
This comment has been minimized.
|
@JulianBirch My proposal still uses the control flow to generate a state machine, and |
This comment has been minimized.
This comment has been minimized.
|
(We could also use this to implement |
This comment has been minimized.
This comment has been minimized.
JulianBirch
commented
Nov 24, 2013
|
Sorry, bit of a thinko. Should have been
Anyway, the point is that there's only one program counter and you can do arbitrarily nasty things. There's some gaps, but I think that gets the idea across. |
This comment has been minimized.
This comment has been minimized.
|
Hm, that might work too, but I'm not sure if all forward/reverse Iterators follow the same kind of control flow, that would need to be investigated. Provided they do though, I'd imagine a syntax like this: yield fn slice_iter<'a, T>(slice: &'a [T]) -> struct SliceIter<'a, T> for &'a T {
let mut start = 0;
let mut end = slice.len();
while start < end {
yield in {
[>..] => { let x = &slice[start]; start += 1; x }
[..<] => { let x = &slice[end-1]; end -= 1; x }
}
}
}That is, provide fork points in the control flow where iterating from the front vs from the back should cause different state changes. I also on purpose picked exotic syntax instead of using something simpler like boolean values: While discussing the topic of double ended iterators, people repeatedly confuse how A |
This comment has been minimized.
This comment has been minimized.
|
I don't really think double ended iterators are that common when you'd want a generator, and I don't think generators should become sugar for iterators in general. Most things I've wanted generators for (usually processing text of some sort) don't make sense double ended. |
This comment has been minimized.
This comment has been minimized.
|
I've been thinking a bit about co-routines lately, and started putting together a Bikeshed/RFC proposal: |
This comment has been minimized.
This comment has been minimized.
JulianBirch
commented
Dec 9, 2013
|
For the record, I agree with @cmr. |
This comment has been minimized.
This comment has been minimized.
|
@vadimcn looks fine to me, though I haven't put as much thought into this as others have. How would the proposal change with the recent closure reform? |
This comment has been minimized.
This comment has been minimized.
|
@cmr: Good question. My intention is that coroutines can be both stack- and heap- allocated, as inferred from usage. However these days closure storage is not automatically inferred, so this plan probably won't work. Also, unlike proc's, coroutines are not "once fn's", so I am not sure what the type of a heap-allocated coroutine should be. I think I'll have to wait out to see how the whole closure reform / Fn traits thing turns out... |
This comment has been minimized.
This comment has been minimized.
|
I note that |
This comment has been minimized.
This comment has been minimized.
|
@farcaller I think having |
This comment has been minimized.
This comment has been minimized.
|
C++ coroutine implementations that I'm aware of are using switch {} "hack" of C that allows you to put the case statement literally everywhere inside the block. I don't think it's possible to do something similar in rust though. |
erickt
referenced this issue
Jul 7, 2014
Closed
Should we track incomplete ideas here instead of in the rust issue tracker? #158
This comment has been minimized.
This comment has been minimized.
|
BTW, I did submit my proposal as Coroutines RFC. Was not accepted. |
This comment has been minimized.
This comment has been minimized.
|
One way to help support this would be to expose stack frames as explicit objects from the language. That could potentially help a library-based syntax extension convert a function to a state machine for a generator, or to implement full coroutines with their own stacks, or even delimited continuations. It could also be a hook for a library-based GC. |
This comment has been minimized.
This comment has been minimized.
|
Just trying to recap this now, and realised that coroutines implemented with a C |
This comment has been minimized.
This comment has been minimized.
|
So for me, the point is to have very lightweight single threaded co-operative multitasking, similar to Protothreads. And now I just realised one more thing. In fact, it would be rather correct to say that Protothreads had been invented to ease coding of complex state machines in C. Thereby, in theory, if one defines a few simple macros that generate a single-threaded state machine code in rust, that would a good enough replacement. Although, the task of implementing such macros would be eased if |
This comment has been minimized.
This comment has been minimized.
|
Should this be moved to the rust-lang/rfcs repo? I don't think this is actionable without having an RFC accepted first. |
This comment has been minimized.
This comment has been minimized.
thestinger
closed this
Oct 10, 2014
This comment has been minimized.
This comment has been minimized.
|
@thestinger, closed RFCs should now have corresponding tracking issues in the RFC repo, and one does not exist for the RFC you linked to as it was closed before we started making tracking issues. In the meantime, I'd like to leave this open so it can be migrated. @nick29581, can you migrate this issue and close it afterwards? |
alexcrichton
reopened this
Oct 10, 2014
This comment has been minimized.
This comment has been minimized.
|
Is this related to rust-lang/rfcs#105? |
This comment has been minimized.
This comment has been minimized.
|
@hatahet: No, not really. |
This comment has been minimized.
This comment has been minimized.
|
This issue has been moved to the RFCs repo: rust-lang/rfcs#388 |
bstrie commentedJul 12, 2013
@thestinger has mentioned this several times as a wishlist item. This is a feature that greatly simplifies the implementation of external iterators by allowing them to be compiled to state machines at compile time (did I get that right?). See http://msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx for reference.
While we shouldn't expect this for 1.0, it might be prescient to reserve the keyword right now.
Nominating for far-future milestone.