Yeah, I think simplicity wins here since the labelled switch ends up becoming another style of iteration rather than an iterator which is similar yet with subtle difference despite a cool behavior.
For me, iterator provides an abstraction to how index or pointer are moved in loops so that we can iterate a collection without manually update the index, something like this:
var iter = Iter.init(...); // Depends on the type: it can be a range or a collection.
// yay! we don't need to handle the indexing since the iterator handles it
while (iter.next()) |iter_data|{
...
}
The pattern from the Op was extremely useful for one of my problems in a gui application where I need to handle a waveform canvas where the mouse may draw forward and backward. However, I didn’t thought of the iterator approach, I ended up swapping the numbers just to comply the for loop:
var sample_idx_start: usize = @intFromFloat(@round(std.math.clamp((idx_start / waveform_canvas_rect.w) * sample_frame_len, 0, sample_frame_len - 1)));
var sample_idx_end: usize = @intFromFloat(@round(std.math.clamp(idx_end / waveform_canvas_rect.w * sample_frame_len, 0, sample_frame_len)));
// we shall retain the orientation of the starting and ending index;
// otherwise, zig will crash if the ending index is smaller.
if (idx_end < idx_start) {
std.mem.swap(usize, &sample_idx_start, &sample_idx_end);
}
const intepret_delta = (mag_end - mag_start) / @as(f32, @floatFromInt((sample_idx_end - sample_idx_start)));
for (sample_idx_start..sample_idx_end, 0..) |idx, i| {
const interpret_y = mag_start + @as(f32, @floatFromInt(i)) * intepret_delta;
frame_group.getSelectTimeFrame().?[idx].re = std.math.clamp(interpret_y, -1, 1);
}
With Op’s pattern, I could simply create an iterator with return an iteration struct containing both required indices:
var sample_idx_start: usize = @intFromFloat(@round(std.math.clamp((idx_start / waveform_canvas_rect.w) * sample_frame_len, 0, sample_frame_len - 1)));
var sample_idx_end: usize = @intFromFloat(@round(std.math.clamp(idx_end / waveform_canvas_rect.w * sample_frame_len, 0, sample_frame_len)));
const intepret_delta = (mag_end - mag_start) / @as(f32, @floatFromInt((sample_idx_end - sample_idx_start)));
// it could return a struct for both array index and the interpreted sample amount
var iter = IntIter.init(sample_idx_start, sample_idx_end, intepret_delta);
while (iter.next()) |item| {
frame_group.getSelectTimeFrame().?[item.idx].re = std.math.clamp(item.interpret_y, -1, 1);
}
This also suggests I could generalize the solution with a single iterator type that provide the start, end indices and the delta of each steps, instead of writing multiple iteration of the same kind with slight variant (which was a problem I faced in that project). I also no longer need to use comment to explain why I suddenly do a memory swap just to reminds not to accidentally remove the swap in the future commits.