On Monday, 2 June 2014 at 20:09:12 UTC, Edwin van Leeuwen wrote:
I'm probably missing something basic, but I am confused by what is going on in the following code.

unittest {
    size_t delegate()[size_t] events;
    foreach( i; 1..4 ) {
        events[i] = { return i; };
    }
    writeln( events[1]() ); // This outputs 3
    assert( events[1]() == 1 );
}

I thought that i should be copied from the local scope and therefore when I call events[1]() the return value should be 1, but in this case it is 3 (it always seems to be the last value of i in the loop).

Cheers, Edwin

Yeah this is a known problem. At the moment the foreach loop is internally rewritten to something like

int i = 1;
while(i < 4)
{
  // foreach loop body

  ++i;
}

While it usually would be preferrable if it were rewritten as

int __compilergeneratedname = 1;
while(__compilergeneratedname < 4)
{
  auto i = __compilergeneratedname++;

  // foreach loop body
}

With the current approach every delegate refers to the same variable. Another problem is that it's possible to alter the iteration index from inside the foreach body.

As you may have guessed, a workaround is to copy the iteration variable yourself:

unittest {
    size_t delegate()[size_t] events;
    foreach(_i; 1..4 ) {
        auto i = _i;
        events[i] = { return i; };
    }
    assert( events[1]() == 1 );
}

This should work though it's less than ideal. There is an open bug report:
https://issues.dlang.org/show_bug.cgi?id=8621

Reply via email to