I've been working on writing an iterator that is relatively boiler-plate free, akin to how slices can do for (slice) |value| { ... } and for (slice) |value, idx| { ... }. I was pleasantly surprised with how well everything composes in general, but a bit of feedback on how it could be nicer:
Here is some code for a custom iterator. Notes at bottom.
constIterator=struct {
count: i32,
current: i32,
pubfnnext(self: &Iterator) ?i32 {
if (self.current>=self.count) returnnull;
constresult=self.current;
self.current+=1;
returnresult;
}
constWithIndex=struct {
value: i32,
idx: i32
};
pubfnnextWithIndex(self: &Iterator) ?WithIndex {
// It'd be much more natural to write this with an early return.if (self.next()) |value| {
returnWithIndex {
.value=value,
.idx=1
};
}
returnnull;
}
};
fnbuildIterator(count: i32) Iterator {
returnIterator {
.count=count,
.current=0
};
}
fnuseCase() void {
{ varit=buildIterator(10); while (it.next()) |i| {
std.debug.warn("i={}\n", i);
} }
// It'd be great if `|e|` could be `|value, index|`
{ varit=buildIterator(10); while (it.nextWithIndex()) |e| {
std.debug.warn("value={}, idx={}\n", e.value, e.idx);
} }
}In nextWithIndex, an early return would read more cleanly, especially when code starts getting complicated and there are a lot of reasons to exit the function. However, the unwrapping if makes this hard to write ; maybe if can adjust the type for the rest of the block so it is no longer optional?
The while loops in useCase are very ugly to look at (the double } }) and require a lot more typing than the typical for (init; cond; increment) loop.
Instead of using a proxy type WithIndex, it'd be great if the |e| could unwrap directly to |value, idx|. Two quick ideas on how this could be done:
- multiple return values
- simple name-based pattern matching
Another option is to standardize this type of thing into the compiler so for (foo) |x| looks for a method with a specific name/result-type, ie, for (foo) |x| ==> next1() IteratorResult(u32), for (foo) |x, y| ==> next2() IteratorResult(u32, bool), etc.
I've been working on writing an iterator that is relatively boiler-plate free, akin to how slices can do
for (slice) |value| { ... }andfor (slice) |value, idx| { ... }. I was pleasantly surprised with how well everything composes in general, but a bit of feedback on how it could be nicer:Here is some code for a custom iterator. Notes at bottom.
In
nextWithIndex, an early return would read more cleanly, especially when code starts getting complicated and there are a lot of reasons to exit the function. However, the unwrappingifmakes this hard to write ; maybeifcan adjust the type for the rest of the block so it is no longer optional?The while loops in
useCaseare very ugly to look at (the double} }) and require a lot more typing than the typicalfor (init; cond; increment)loop.Instead of using a proxy type
WithIndex, it'd be great if the|e|could unwrap directly to|value, idx|. Two quick ideas on how this could be done:Another option is to standardize this type of thing into the compiler so
for (foo) |x|looks for a method with a specific name/result-type, ie,for (foo) |x| ==> next1() IteratorResult(u32),for (foo) |x, y| ==> next2() IteratorResult(u32, bool), etc.