D2 closure and loop variables

Robert Jacques sandford at jhu.edu
Sat Oct 31 21:54:46 PDT 2009


On Sat, 31 Oct 2009 20:52:05 -0400, Adam D. Ruppe  
<destructionator at gmail.com> wrote:

> I'm trying to make a delegate thing in a loop and hit something that  
> seemed intuitively wrong. Here's some short code that shows what I mean:
>
> ===
> import std.stdio;
>
> class A {
> 	this(void delegate() _a) {
> 		a = _a;
> 	}
>
> 	void run() { a(); }
>
> 	private void delegate() a;
> }
>
> void main() {
> 	auto list = ['a', 'b', 'c'];
> 	A[string] buttons;
> 	foreach(l; list) {
> 		buttons[l ~ "\n"] = new A( { writefln("%s", l); } );
> 	}
>
> 	auto a = readln();
> 	buttons[a].run;
> }
> ===
>
> What happens is no matter what key you press, you always get the output  
> of "c", whereas at first glance, I'd expect it to echo back the same  
> letter to you. This makes sense when thinking about it in terms of  
> memory: the delegate accesses the memory that variable l had assigned to  
> it, which was overwritten by later loop iterations.
>
> But, while it makes sense when thinking about it, intuitively, I  
> expected that loop variable to be copied over somewhere, so the delegate  
> would be accessing its own private copy, not the pointer being  
> overwritten by the loop.
>
>
> Generally, if a delegate escapes any scope, I expect it to take a  
> snapshot of  the stack it is referencing at that time to make its  
> private copy at the time it passes the closing brace.
>
>
> What I'm asking is:
>
> a) Is my expectation wrong, or does that make sense?
> b) If not wrong, should D be doing this now, or is this a bug?
> and c) If it isn't a bug, should it be? Copying it on every loop  
> iteration would have a definite performance penalty, so it isn't  
> remotely free. Moreover, being able to reference variables that might  
> change later can be likely a good thing:
>
> [code]
> SomeClass a;
>
> foreach(item; collection)
>   item.someDelegate = { a.doSomething;}
>
> a = new SomeClass;
> [/code]
>
> If it make a private copy inside that loop, someDelegate would be  
> looking at null the whole time, whereas with the current behaviour, this  
> code works.
>
> So changing it might not even be sane from that perspective.
>
>
> What do you guys think?

For me, this makes sense and isn't a bug. A closure should be able to  
mutate it's enclosing scope and be affected by mutation. That said, a  
enhancement that allowed variables to copied by value, instead of by  
reference, might be nice. As for your particular issue, since it looks  
like you're doing GUI stuff, you might want to take a look at DFL. One of  
the things it does it to has all it's delegates take an object and an args  
object. As most of DFL's objects support tags, this allows you to retrieve  
custom information from the delegate arguments (though casting all the  
time does get ugly)



More information about the Digitalmars-d mailing list