On 15.06.19 18:29, Rémy Mouëza wrote:
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.

It's a bug. It's memory corruption. Different objects with overlapping lifetimes use the same memory location.

The same behavior happens in Python.

No, it's not the same. Python has no sensible notion of variable scope.

>>> for i in range(3): pass
...
>>> print(i)
2

Yuck.

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.
...

It's not the same instance of the variable. Foreach loop variables are local to the loop body. They may both be called `x`, but they are not the same. It's most obvious with `immutable` variables.

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.
...

No, this is not an issue of by value vs by reference. All captures in D are by reference, yet the behavior is wrong.

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.
...

By reference capturing may be a convenient default, but even capturing by reference the behavior is wrong.



Reply via email to