foreach over pointers OR foreach that mutates the iterator

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Wed Jan 25 12:22:54 PST 2017


On Wednesday, January 25, 2017 16:15:49 Las via Digitalmars-d wrote:
> So the reference says that (when range has the properties)
> `foreach (e; range) { ... }` is equivalent to:
> for (auto __r = range; !__r.empty; __r.popFront())
> {
>      auto e = __r.front;
>      ...
> }
>
> Though this isn't always true, as when I use this struct:
> struct S {
>   int front = 10;
>   void popFront() {
>       --front;
>   }
>   @property bool empty() {
>       return front == 0;
>   }
> }
>
> then I can do this:
> void main() {
>   S s;
>   auto p = &s;
>   p.popFront;
>   writeln(p.front);
> }
>
> But not this:
> void main() {
>   S s;
>   auto p = &s;
>   foreach(i; p)
>       writeln(i);
> }
>
> x.d(18): Error: invalid foreach aggregate p
> Failed: ["dmd", "-v", "-c",
> "-of/tmp/.rdmd-1000/rdmd-x.d-032B33C4A922C519594F67AF08DBF6C9/objs/x.o",
> "x.d", "-I."]

Given that

    static assert(isInputRange!(typeof(&s)));

compiles, the fact that the second foreach doesn't work is arguably a bug.

> Related:
> UFCS does not work on pointers, which matters because of
> std.range.primitives.

It only matters if you're trying to define the range primitives for your
range as free functions for some reason. Just put them on the type itself
and be done with it. The only reason that wouldn't work would be if you
weren't in control of the code for the type in question, and I'm inclined to
think that turning a type that isn't a range into a range using free
functions isn't the best of ideas. You can always wrap the type in another
type though if you really want to turn it into a range and can't just
because you're dealing with a pointer.

- Jonathan M Davis



More information about the Digitalmars-d mailing list