Strange closure behaviour

Rémy Mouëza remy.moueza at gmail.com
Sat Jun 15 16:29:29 UTC 2019


On Saturday, 15 June 2019 at 01:21:46 UTC, Emmanuelle wrote:
> On Saturday, 15 June 2019 at 00:30:43 UTC, Adam D. Ruppe wrote:
>> On Saturday, 15 June 2019 at 00:24:52 UTC, Emmanuelle wrote:
>>> Is it a compiler bug?
>>
>> Yup, a very longstanding bug.
>>
>> You can work around it by wrapping it all in another layer of 
>> function which you immediately call (which is fairly common in 
>> javascript):
>>
>>         funcs ~= ((x) => (int i) { nums[x] ~= i; })(x);
>>
>> Or maybe less confusingly written long form:
>>
>>         funcs ~= (delegate(x) {
>>             return (int i) { nums[x] ~= i; };
>>         })(x);
>>
>> You write a function that returns your actual function, and 
>> immediately calls it with the loop variable, which will 
>> explicitly make a copy of it.
>
> Oh, I see. Unfortunate that it's a longstanding compiler bug, 
> but at least the rather awkward workaround will do. Thank you!

I don't know if we can tell this is a compiler bug. The same 
behavior happens in Python. The logic being variable `x` is 
captured by the closure. That closure's context will contain a 
pointer/reference to x. Whenever x is updated outside of the 
closure, the context still points to the modified x. Hence the 
seemingly strange behavior.

Adam's workaround ensures that the closure captures a temporary 
`x` variable on the stack: a copy will be made instead of taking 
a reference, since a pointer to `x` would be dangling once the 
`delegate(x){...}` returns.

Most of the time, we want a pointer/reference to the enclosed 
variables in our closures. Note that C++ 17 allows one to select 
the capture mode: the following link lists 8 of them: 
https://en.cppreference.com/w/cpp/language/lambda#Lambda_capture.

D offers a convenient default that works most of the time. The 
trade-off is having to deal with the creation of several closures 
referencing a variable being modified in a single scope, like the 
incremented `x` of the for loop.

That said, I wouldn't mind having the compiler dealing with that 
case: detecting that `x` is within a for loop and making copies 
of it in the closures contexts.


More information about the Digitalmars-d-learn mailing list