-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
Here's my implementation of generators in Zig 0.6.0. As far as I can tell there's no elegant way in current Zig to eliminate the intermediate copy of all values yielded by the generator. There's also a less important issue where the ritual for yielding a value is a bit ugly:
item.* = VALUE;
suspend;I'm going to propose something that should fix both problems, and which I think is the most minimal change to make generators a proper feature. The idea is to add a pointer parameter to the calling convention for (some?) async functions, one which gets written to by yield VALUE statements. Some pseudocode:
fn foo() void yield u8 {
var n: u8 = 0;
while (true) {
yield n;
n += 1;
}
}
fn bar() {
var item: u8;
var frame = async(&item) foo();
warn("{}\n", .{ item });
resume frame;
warn("{}\n", .{ item });
var item2: u8;
@setYieldLocation(frame, &item2);
resume frame;
warn("{}\n", .{ item2 });
}In this illustrative syntax, the parameter to async sets the pointer that yield writes to. The yield statement writes to that pointer and suspends. @setYieldLocation allows the caller to change the pointer after the initial creation of the frame. Once #2765 is resolved, this should allow a userland wrapper to implement a next() function without requiring an intermediate copy.
This adds quite a bit of surface area to the language. A minimal alternative that requires no language changes besides #2765 is for the userland implementation of next() to communicate the yield pointer to the generator code explicitly. This would require changing the yield ritual from
item.* = VALUE;
suspend;to
item.*.* = VALUE;
suspend;I consider this to be a bit too cumbersome, and it breaks with the ideal of making the right way to code the easiest way as well, since dropping the double-pointer still works, as long as you tolerate the intermediate copy.