Delegate, scope and associative array

Steven Schveighoffer via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Jun 3 06:30:15 PDT 2014


On Mon, 02 Jun 2014 19:43:58 -0400, Rene Zwanenburg  
<renezwanenburg at gmail.com> wrote:

> 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

There is a school of thought (to which I subscribe) that says you  
shouldn't allocate a separate closure for each loop iteration.

Basically, a closure will only use the stack frame of the calling  
function, not any loop iteration.

This way, you can clearly delineate what scope the closure has, and avoid  
unnecessary allocations.

-Steve


More information about the Digitalmars-d-learn mailing list