How to avoid variable capturing in `foreach` loop with lambdas?

Tejas notrealemail at gmail.com
Thu Jan 5 15:36:43 UTC 2023


On Thursday, 5 January 2023 at 11:55:33 UTC, thebluepandabear 
wrote:
> I am using CSFML D bindings and I have created my own sort of 
> UI library for drawing elements onto the screen.
>
> One of the classes I've created is a `Button` class, which 
> contains a delegate called `onButtonClick` which is called when 
> the button is clicked on by the user.
>
> Up until now, everything has worked fine.
>
> I want to add a couple of buttons side by side to represent an 
> element of a list and assign them each a unique lambda 
> expression for that particular element in the list, this is the 
> current code I have:
>
> ```D
> foreach (BoardSize boardSize; arr) {
>     Button button = new Button();
>     button.text = format("%sx%s", boardSize[0], boardSize[1]);
>     button.onButtonClick = {
>         
> eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize);
>     };
>     button.onButtonClick();
>     _boardSizeRow.addChild(button);
> }
> ```
>
> Running this code, I had expected that everything would work 
> fine. Unfortunately upon running the code, tapping each of the 
> buttons returned only the largest `boardSize` value, the one 
> which is gets iterated last.
>
> Note that I am still not totally sure why this is the case. At 
> first, I was confused, but then I suspected that after the 
> variable gets sent as a parameter to the 
> `settingsWindow_onBoardSizeButtonClick` function, it gets 
> changed again in the next iteration creating a sort of chain 
> effect -- I may be wrong, but this is my suspicion.
>
> Some of the things I tried was creating a new object each time, 
> although it didn't work. I might have not done this properly as 
> I am a beginner to D language. I saw someone else ask a similar 
> question as to why this is happening but that was for C#, not 
> D, so it wasn't that much of a use to me.
>
> Help would be appreciated!

Ah, I think you ran into that delegate bug

# 🥲

Try this code in it's place:

```d
  foreach (BoardSize boardSize; arr) (){ // notice the brackets
      Button button = new Button();
      button.text = format("%sx%s", boardSize[0], boardSize[1]);
      button.onButtonClick = {

  eventHandler.settingsWindow_onBoardSizeButtonClick(boardSize);
      };
      button.onButtonClick();
      _boardSizeRow.addChild(button);
  }() // notice the brackets
```
This is not your fault, it's an "optimization" that's being 
performed by dmd so that "the user doesn't get surprised" when 
they realize that capturing every single variable emitted in a 
`foreach` will allocate a new heap closure

Have fun reading this :

https://issues.dlang.org/show_bug.cgi?id=21929



More information about the Digitalmars-d-learn mailing list