Looped closure captured by reference?
Delegates from D seem to capture local values by reference, which has strange side effects when creating closures in loops: at the end, you have n closures with equal context pointers. Take this example:
import std.stdio;
alias Closure = void delegate();
Closure[] closures;
void main(){
foreach(a; ["Je", "Tu", "En", "Oui", "Na"])
closures ~= {write(a);};
foreach(c; closures)
c();
writeln(" batman");
}
Will print NaNaNaNaNa batman
.
Is this the expected behavior? If so, how would I work on it so that it prints all array elements correctly?
It gets even funnier when using a for-loop with a counter variable, at the end i
is equal to the size of the array, and when used closures[i]
in a delegate, it throws an out-of-bounds error.
source to share
Yes, this is the expected behavior (EDIT: ... this is a really old known bug! Https://issues.dlang.org/show_bug.cgi?id=2043 so only expected this happens in this and you can easily get used to it but it shouldn't actually happen) and also seen in other languages, so it's a good principle to know.
To get a separate copy of the variables in the loop, call another function that returns the delegate you want to store by passing the loop variable to it.
import std.stdio;
alias Clojure = void delegate();
Clojure[] clojures;
void main(){
foreach(a; ["Je", "Tu", "En", "Oui", "Na"])
clojures ~= ((b) => { write(b);})(a);
foreach(c; clojures)
c();
writeln(" batman");
}
The line clojures ~= ((b) => { write(b);})(a);
has changed: This defines a swift delegate that the delegate returns. The optional return function closes behind a snapshot of the loop state, not just function-level local variables.
I use a lot in JavaScript too:
function makeHandler(item) {
return function() {
// use item here
};
}
var array = [1,2,3];
for(var I = 0; I < array.length; I++)
foo.addEventListener("click", makeHandler(array[I]));
It does the same thing for the same reason as D, only in a different syntax, and breaks up into larger functions instead of trying to do it as a one-liner.
Let's define a function that returns a function that uses the captured loop variable. At the point of use, we call one function that returns a delegate that is stored later.
The syntax reduced D, ((b) => { write(b);})(a);
, (b) => ...
- a feature makeHandler
visible in javascript. The return value { write(b); }
is shorthand for return function() { ... }
in JS (BTW that the same JS syntax basically works with a different D, you can write a long thing with keywords delegate
or function
. D function
doesn't capture variables, though, delegate
it does.)
Then finally the parentheses around it and (a)
at the end are just a function call. The content inside is the same as makeHandler
calling it (...)(a)
; it is makeHadndler(a)
.
source to share