How to implement a range?

Mike Parker aldacron at gmail.com
Fri Apr 16 06:46:45 UTC 2021


On Friday, 16 April 2021 at 06:21:35 UTC, Jack wrote:
> In order to my array class work with filter, I went to 
> implement an ``InputRange``. But I don't quite get how do that 
> and didn't find much help on the docs. From below code, is 
> ``moveFront()`` implemented correctly? I'm using a simple int i 
> as index of current item and in popFront() just increment it. I 
> must reset the i value once the loop is done, right? where am I 
> supposed to do that? opApply()? not properly reseting it result 
> in obvious bugs like subsequent calls doesn't work because the 
> index is in the end of the array:
>

Generally, you don't want your containers to be ranges 
themselves. You want them to produce ranges, i.e., separate the 
duties of iteration from the duties of the container. Also, it's 
best to make your range types as structs rather than classes for 
an easier time.

A basic input range doesn't need to worry about `moveFront`. So 
you can get away with `empty`, `front`, and `popFront` on a 
struct.

```d
import std.stdio;

class MyArray(T)
{
     private T[] _a;

     this(T[] a) { _a = a; };
     auto opIndex() { return Range(_a[]); }

     private static struct Range {
         T[] a;
         T front() { return a[0]; }
         void popFront() { a = a[1 .. $]; }
         bool empty() { return a.length == 0; }
     }
}

void main()
{
     auto ma = new MyArray!int([10, 20, 30, 44, 55]);
     foreach(i; ma[])
     {
         writeln(i);
     }
}
```

I've overloaded the slice operator via the no-arg `opIndex` to 
provide the range so that you can do `ma[]` to get to it. You'd 
want to expand on that to handle start & end points for a slice.

But anyway, the whole idea behind ranges is you want to keep your 
iteration separate from the data. Then ranges can be copied 
around and consumed without every changing the original data 
structure.


More information about the Digitalmars-d-learn mailing list