On Monday, 6 May 2024 at 16:41:38 UTC, Steven Schveighoffer wrote:
On Monday, 6 May 2024 at 06:29:49 UTC, Liam McGillivray wrote:
Delegates can be a pain, as they often have results different from what one would intuitively expect. This can easily result in bugs.

Here's a line that caused a bug that took me awhile to find:
```
foreach(card; unitCards) card.submitted = delegate() => selectUnit(card.unit);
```

Each `UnitInfoCard` object (which `card` is a member of) contains a `Unit` object called `unit`. The intention of this line was that each object in `unitCards` would call `selectUnit` with it's own `unit` every time it calls `submitted`. Instead, every card calls `submitted` with the *last* value of `card`.

Yes, this is because the foreach loop reuses the same memory slot for `card`.

Even though this is allocated as a closure, it still only allocates the frame stack of the *enclosing function*, and does not allocate a new slot for each loop iteration.

You can force this by using a lambda which allocates the closure:

```d
foreach(card; unitCards)
card.submitted = (c2) { return () => selectUnit(c2.unit); }(card);
```

This is a lambda which accepts `card` as a parameter, and returns an appropriate delegate. It is important to use a parameter, because if you just use card inside there, it's still using the single stack frame of the calling function!

...

I would love to see a solution, but the workaround at least exists!

-Steve

Well that's something. It's not a very good solution for a language that aims for readability. It took me awhile looking at it to figure out what it is about, as I'm not familiar with this syntax.

The solution that I did before seeing this was to add a function to `UnitInfoCard` to give it a delegate with a `Unit unit` parameter, and then that function would give that function with the `unit` parameter set to itself to it's own `submitted` member. I will probably keep it like this for readability.

```
    void clickAction(void delegate(Unit) @safe clickAction) {
        submitted = () => clickAction(unit);
    }
```

Reply via email to