Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
October 05, 2018 Error: variable 'xyz' has scoped destruction, cannot build closure | ||||
---|---|---|---|---|
| ||||
I got the compilation error in the subject line when trying to create a range via std.range.generate. Turns out this was caused by trying to create a closure for 'generate' where the closure was accessing a struct containing a destructor. The fix was easy enough: write out the loop by hand rather than using 'generate' with a closure. What I'm wondering/asking is if there alternate way to do this that would enable the 'generate' approach. This is more curiosity/learning at this point. Below is a stripped down version of what I was doing. I have a struct for output buffering. The destructor writes any data left in the buffer to the output stream. This gets passed to routines performing output. It was this context that I created a generator that wrote to it. ----example.d----- struct BufferedStdout { import std.array : appender; private auto _outputBuffer = appender!(char[]); ~this() { import std.stdio : write; write(_outputBuffer.data); _outputBuffer.clear; } void appendln(T)(T stuff) { import std.range : put; put(_outputBuffer, stuff); put(_outputBuffer, "\n"); } } void foo(BufferedStdout output) { import std.algorithm : each; import std.conv : to; import std.range: generate, takeExactly; import std.random: Random, uniform, unpredictableSeed; auto randomGenerator = Random(unpredictableSeed); auto randomNumbers = generate!(() => uniform(0, 1000, randomGenerator)); auto tenRandomNumbers = randomNumbers.takeExactly(10); tenRandomNumbers.each!(n => output.appendln(n.to!string)); } void main(string[] args) { foo(BufferedStdout()); } ----End of example.d----- Compiling the above results in: $ dmd example.d example.d(22): Error: variable `example.foo.output` has scoped destruction, cannot build closure As mentioned, using a loop rather than 'generate' works fine, but help with alternatives that would use generate would be appreciated. The actual buffered output struct has more behind it than shown above, but not too much. For anyone interested it's here: https://github.com/eBay/tsv-utils/blob/master/common/src/tsvutil.d#L358 |
October 05, 2018 Re: Error: variable 'xyz' has scoped destruction, cannot build closure | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jon Degenhardt | On Friday, 5 October 2018 at 03:27:17 UTC, Jon Degenhardt wrote: > I got the compilation error in the subject line when trying to create a range via std.range.generate. Turns out this was caused by trying to create a closure for 'generate' where the closure was accessing a struct containing a destructor. > > The fix was easy enough: write out the loop by hand rather than using 'generate' with a closure. What I'm wondering/asking is if there alternate way to do this that would enable the 'generate' approach. This is more curiosity/learning at this point. > > Below is a stripped down version of what I was doing. I have a struct for output buffering. The destructor writes any data left in the buffer to the output stream. This gets passed to routines performing output. It was this context that I created a generator that wrote to it. > > ----example.d----- > struct BufferedStdout > { > import std.array : appender; > > private auto _outputBuffer = appender!(char[]); > > ~this() > { > import std.stdio : write; > write(_outputBuffer.data); > _outputBuffer.clear; > } > > void appendln(T)(T stuff) > { > import std.range : put; > put(_outputBuffer, stuff); > put(_outputBuffer, "\n"); > } > } > > void foo(BufferedStdout output) > { > import std.algorithm : each; > import std.conv : to; > import std.range: generate, takeExactly; > import std.random: Random, uniform, unpredictableSeed; > > auto randomGenerator = Random(unpredictableSeed); > auto randomNumbers = generate!(() => uniform(0, 1000, randomGenerator)); > auto tenRandomNumbers = randomNumbers.takeExactly(10); > tenRandomNumbers.each!(n => output.appendln(n.to!string)); > } > > void main(string[] args) > { > foo(BufferedStdout()); > } > ----End of example.d----- > > Compiling the above results in: > > $ dmd example.d > example.d(22): Error: variable `example.foo.output` has scoped destruction, cannot build closure > > As mentioned, using a loop rather than 'generate' works fine, but help with alternatives that would use generate would be appreciated. > > The actual buffered output struct has more behind it than shown above, but not too much. For anyone interested it's here: https://github.com/eBay/tsv-utils/blob/master/common/src/tsvutil.d#L358 tenRandomNumbers.each!((n,o) => o.appendln(n.to!string))(output); or tenRandomNumbers.each!((n, ref o) => o.appendln(n.to!string))(output); should hopefully do the trick (run.dlang.io seems to be down atm). The problem is that `output` is captured by the delegate and this somehow causes problems (idk what or why). If the (perviously captured) variables are instead passed as parameters then there is no need to create a closure for the variable, so no problem. This is also the way to ensure that lambdas are @nogc if you ever need that. |
October 05, 2018 Re: Error: variable 'xyz' has scoped destruction, cannot build closure | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nicholas Wilson | On Friday, 5 October 2018 at 06:22:57 UTC, Nicholas Wilson wrote:
> tenRandomNumbers.each!((n,o) => o.appendln(n.to!string))(output);
>
> or
>
> tenRandomNumbers.each!((n, ref o) => o.appendln(n.to!string))(output);
>
> should hopefully do the trick (run.dlang.io seems to be down atm).
>
Alas is does not because each does not accept additional argument other than the range. Shouldn't be hard to fix though.
|
October 05, 2018 Re: Error: variable 'xyz' has scoped destruction, cannot build closure | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nicholas Wilson | On Friday, 5 October 2018 at 06:44:08 UTC, Nicholas Wilson wrote:
> On Friday, 5 October 2018 at 06:22:57 UTC, Nicholas Wilson wrote:
>> tenRandomNumbers.each!((n,o) => o.appendln(n.to!string))(output);
>>
>> or
>>
>> tenRandomNumbers.each!((n, ref o) => o.appendln(n.to!string))(output);
>>
>> should hopefully do the trick (run.dlang.io seems to be down atm).
>>
>
> Alas is does not because each does not accept additional argument other than the range. Shouldn't be hard to fix though.
Yeah, that's what I was seeing also. Thanks for taking a look. Is there perhaps a way to limit the scope of the delegate to the local function? Something that would tell the compiler the delegate has a lifetime shorter than the struct.
One specific it points out is that this a place where the BufferedOutputRange I wrote cannot be used interchangeably with other output ranges. It's minor, but the intent was to be able to pass this anyplace an output range could be used.
|
October 05, 2018 Re: Error: variable 'xyz' has scoped destruction, cannot build closure | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nicholas Wilson | On Friday, 5 October 2018 at 06:44:08 UTC, Nicholas Wilson wrote: > Alas is does not because each does not accept additional argument other than the range. Shouldn't be hard to fix though. https://issues.dlang.org/show_bug.cgi?id=19287 |
October 05, 2018 Re: Error: variable 'xyz' has scoped destruction, cannot build closure | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nicholas Wilson | On Friday, 5 October 2018 at 06:56:49 UTC, Nicholas Wilson wrote: > On Friday, 5 October 2018 at 06:44:08 UTC, Nicholas Wilson wrote: >> Alas is does not because each does not accept additional argument other than the range. Shouldn't be hard to fix though. > > https://issues.dlang.org/show_bug.cgi?id=19287 You can thread multiple arguments through to `each` using `std.range.zip`: tenRandomNumbers .zip(repeat(output)) .each!(unpack!((n, output) => output.appendln(n.to!string))); Full code: https://run.dlang.io/is/Qe7uHt |
October 05, 2018 Re: Error: variable 'xyz' has scoped destruction, cannot build closure | ||||
---|---|---|---|---|
| ||||
Posted in reply to Paul Backus | On Friday, 5 October 2018 at 16:34:32 UTC, Paul Backus wrote:
> On Friday, 5 October 2018 at 06:56:49 UTC, Nicholas Wilson wrote:
>> On Friday, 5 October 2018 at 06:44:08 UTC, Nicholas Wilson wrote:
>>> Alas is does not because each does not accept additional argument other than the range. Shouldn't be hard to fix though.
>>
>> https://issues.dlang.org/show_bug.cgi?id=19287
>
> You can thread multiple arguments through to `each` using `std.range.zip`:
>
> tenRandomNumbers
> .zip(repeat(output))
> .each!(unpack!((n, output) => output.appendln(n.to!string)));
>
> Full code: https://run.dlang.io/is/Qe7uHt
Very interesting, thanks. It's a clever way to avoid the delegate capture issue.
(Aside: A nested function that accesses 'output' from lexical context has the same issue as delegates wrt to capturing the variable.)
|
October 05, 2018 Re: Error: variable 'xyz' has scoped destruction, cannot build closure | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jon Degenhardt | On Friday, 5 October 2018 at 19:31:56 UTC, Jon Degenhardt wrote:
> On Friday, 5 October 2018 at 16:34:32 UTC, Paul Backus wrote:
>> You can thread multiple arguments through to `each` using `std.range.zip`:
>>
>> tenRandomNumbers
>> .zip(repeat(output))
>> .each!(unpack!((n, output) => output.appendln(n.to!string)));
>>
>> Full code: https://run.dlang.io/is/Qe7uHt
>
> Very interesting, thanks. It's a clever way to avoid the delegate capture issue.
>
> (Aside: A nested function that accesses 'output' from lexical context has the same issue as delegates wrt to capturing the variable.)
Note that this solution may do a lot of output and hence running of the destructor.
Use: `.zip(repeat(&output))` to avoid that.
|
Copyright © 1999-2021 by the D Language Foundation