# Iterators

> A convention, not an interface.

```zig
const std = @import("std");
const expect = std.testing.expect;

// Zig has no Iterator trait. A type is iterable if it has `next()` returning
// an optional, because that is what `while (it.next()) |x|` needs.
const Countdown = struct {
    remaining: u32,

    pub fn next(self: *Countdown) ?u32 {
        if (self.remaining == 0) return null;
        self.remaining -= 1;
        return self.remaining;
    }
};

test "write your own iterator" {
    var it = Countdown{ .remaining = 3 };
    var sum: u32 = 0;
    while (it.next()) |value| sum += value;
    try expect(sum == 3); // 2 + 1 + 0
}

test "split on a single byte" {
    var it = std.mem.splitScalar(u8, "a,b,c", ',');
    try expect(std.mem.eql(u8, it.next().?, "a"));
    try expect(std.mem.eql(u8, it.next().?, "b"));
    try expect(std.mem.eql(u8, it.next().?, "c"));
    try expect(it.next() == null);
}

test "split keeps empty fields, tokenize does not" {
    var split = std.mem.splitScalar(u8, "a,,b", ',');
    var split_count: u32 = 0;
    while (split.next()) |_| split_count += 1;
    try expect(split_count == 3); // "a", "", "b"

    var tokens = std.mem.tokenizeScalar(u8, "a,,b", ',');
    var token_count: u32 = 0;
    while (tokens.next()) |_| token_count += 1;
    try expect(token_count == 2); // "a", "b"
}

test "tokenize on any of several separators" {
    var it = std.mem.tokenizeAny(u8, "one two\tthree", " \t");
    try expect(std.mem.eql(u8, it.next().?, "one"));
    try expect(std.mem.eql(u8, it.next().?, "two"));
    try expect(std.mem.eql(u8, it.next().?, "three"));
}

test "window and chunk a slice" {
    var windows = std.mem.window(u8, "abcd", 2, 1); // overlapping
    try expect(std.mem.eql(u8, windows.next().?, "ab"));
    try expect(std.mem.eql(u8, windows.next().?, "bc"));
}
```

*Runnable: compiled to WebAssembly and executed by CI against Zig master. (`03-standard-library.iterators`)*

Zig has no `Iterator` trait, no `IntoIterator`, no iterator protocol to
implement. A type is iterable if it has a `next()` method returning an
optional, because that is exactly what this loop needs:

```zig
while (it.next()) |value| { ... }
```

Writing your own is a struct with state and a `next`. That is the whole
mechanism.

## Splitting text

| Function | Behaviour |
| --- | --- |
| `splitScalar(u8, s, ',')` | split on one byte, **keeps** empty fields |
| `splitAny(u8, s, ",;")` | split on any of several bytes |
| `splitSequence(u8, s, "::")` | split on a multi-byte separator |
| `tokenizeScalar(u8, s, ',')` | same, but **skips** empty fields |
| `tokenizeAny(u8, s, " \t")` | skip empties, any of several separators |

The split/tokenize distinction is the one to remember: parsing CSV wants
`split` (an empty field is meaningful); splitting on whitespace wants
`tokenize` (runs of spaces should not produce empty tokens).

## Windows and chunks

`std.mem.window(T, s, size, advance)` yields overlapping views;
`std.mem.chunk` yields non-overlapping ones. Both are useful for n-gram and
block processing without copying.

## No lazy adapter chains

There is no `.map().filter().take()`. Write the loop: it is usually shorter
than the chain, and there is nothing hidden about its cost.
