-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
A yield
construct in the vein of C#
#7746
Comments
Seconded. Hopefully not too far in the future. |
@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 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:
|
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. |
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 ( |
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. |
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 |
Given that #5633 was assigned to Far-Future and was closed in favor of this bug, I'm assigning that milestone here. |
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. |
@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. |
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. |
@Kimundi: Sounds good, but should be optional. Original syntax for single-ended iterators. |
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. |
@JulianBirch My proposal still uses the control flow to generate a state machine, and |
(We could also use this to implement |
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. |
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 |
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. |
I've been thinking a bit about co-routines lately, and started putting together a Bikeshed/RFC proposal: |
For the record, I agree with @cmr. |
@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? |
@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... |
I note that |
@farcaller I think having |
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. |
BTW, I did submit my proposal as Coroutines RFC. Was not accepted. |
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. |
Just trying to recap this now, and realised that coroutines implemented with a C |
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 |
Should this be moved to the rust-lang/rfcs repo? I don't think this is actionable without having an RFC accepted first. |
@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? |
Is this related to rust-lang/rfcs#105? |
@hatahet: No, not really. |
This issue has been moved to the RFCs repo: rust-lang/rfcs#388 |
…od-calls-suggestion, r=flip1995 Fix `redundant_closure_for_method_calls` suggestion Fixes rust-lang#7746. The issue turns out to be more general than raw pointers. The `redundant_closure_for_method_calls` lint produces incorrect suggestions when the method is associated with a type that must be enclosed in angle brackets or must be written with generic arguments substituted. For example: ```rust fn main() { // Clippy's suggestion: [T; N]::as_slice // Correct suggestion: <[u8; 3]>::as_slice let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]); array_opt.map(|a| a.as_slice()); // Clippy's suggestion: [T]::len // Correct suggestion: <[u8]>::len let slice_opt: Option<&[u8]> = Some(b"slice"); slice_opt.map(|s| s.len()); // Clippy's suggestion: *const T::is_null // Correct suggestion: <*const usize>::is_null let ptr_opt: Option<*const usize> = Some(&487); ptr_opt.map(|p| p.is_null()); // Clippy's suggestion: dyn TestTrait::method_on_dyn // Correct suggestion: <dyn TestTrait>::method_on_dyn let test_struct = TestStruct {}; let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct); dyn_opt.map(|d| d.method_on_dyn()); } // For the trait object example: trait TestTrait {} struct TestStruct {} impl TestTrait for TestStruct {} impl dyn TestTrait + '_ { fn method_on_dyn(&self) -> bool { false } } ``` The issue also affects references and tuples, though I had to patch the standard library with non-trait methods for those types to test that. Just in case, I also included handling for `!`, since it appeared to be possible to call methods on it with angle brackets. I just couldn't verify the resulting suggestion, since dead-code analysis eliminates the code first. This is my first exposure to Rust compiler internals, so please let me know if I'm taking the wrong approach here! changelog: [`redundant_closure_for_method_calls`]: add angle brackets and substitute generic arguments in suggestion when needed
@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.
The text was updated successfully, but these errors were encountered: