Skip to content
Ranges in Depth
step 1/5

Reading — step 1 of 5

Learn

~1 min readTemplates and Ranges

D's ranges replace iterators with a simpler 3-method protocol:

struct InputRange {
    bool empty();
    T front();
    void popFront();
}

Any type with these methods works with foreach, Range.map, etc.

Custom range:

struct Counter {
    int current = 0;
    int max;

    bool empty() { return current >= max; }
    int front() { return current; }
    void popFront() { current++; }
}

foreach (n; Counter(0, 5)) writeln(n);   // 0, 1, 2, 3, 4

Range types (capability levels):

  • InputRange — single forward pass
  • ForwardRange — can save and replay (save())
  • BidirectionalRange — can iterate from either end
  • RandomAccessRange[i] indexing in O(1)

Most arrays satisfy RandomAccessRange.

Composing ranges with std.range and std.algorithm:

import std.range;
import std.algorithm;
import std.array;

auto result = iota(1, 11)              // [1, 2, ..., 10]
    .filter!(n => n % 2 == 0)
    .map!(n => n * n)
    .take(3)
    .array;                            // [4, 16, 36]

Ranges are lazy — nothing computes until consumed. The .array materializes; .filter!, .map!, .take are all wrappers.

Useful helpers:

  • iota(start, end) — like Python's range
  • chain(a, b) — concatenate
  • zip(a, b) — pair up
  • enumerate(r) — yield (index, element)
  • chunks(r, n) — break into N-sized pieces

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…