On Wednesday, 25 January 2017 at 16:15:49 UTC, Las 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."]
Why should this work? Because some times I want foreach to
modify the iterator, because some times I like to have an inner
foreach that uses the same iterator as the outer one, to
effectively still iterate over the same range, but change the
contents of the loop.
Bad example:
foreach(i; &range) {
writeln(i);
if(i > 2) foreach(i; &range) {
writeln(i * 3);
if(i < 10)
break;
}
}
This loop would change behavior each time one of the 'if's pass.
An alternative would be to implement a new foreach, perhaps
&foreach, that does this instead:
for (/+ NB: We are not copying the range! +/; !range.empty;
range.popFront())
{
auto e = range.front;
...
}
Related:
UFCS does not work on pointers, which matters because of
std.range.primitives.
Thoughts?
Not sure if this is a bug in isInputRange or foreach, but they
should work consistently. Anyway, another solution is to use
refRange:
void main() {
import std.range : refRange;
S s;
auto p = refRange(&s);
foreach(i; p)
writeln(i);
}
That way you don't need to dereference 'p' everytime you want to
iterate over it. Plus, it should compose well with other range
wrappers / algorithms.