digitalmars.D.learn - Error: variable 'xyz' has scoped destruction, cannot build closure
- Jon Degenhardt (58/58) Oct 04 2018 I got the compilation error in the subject line when trying to
- Nicholas Wilson (12/71) Oct 04 2018 tenRandomNumbers.each!((n,o) => o.appendln(n.to!string))(output);
- Nicholas Wilson (3/10) Oct 04 2018 Alas is does not because each does not accept additional argument
- Jon Degenhardt (9/24) Oct 04 2018 Yeah, that's what I was seeing also. Thanks for taking a look. Is
- Nicholas Wilson (2/4) Oct 04 2018 https://issues.dlang.org/show_bug.cgi?id=19287
- Paul Backus (8/13) Oct 05 2018 You can thread multiple arguments through to `each` using
- Jon Degenhardt (6/22) Oct 05 2018 Very interesting, thanks. It's a clever way to avoid the delegate
- Nicholas Wilson (4/19) Oct 05 2018 Note that this solution may do a lot of output and hence running
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
Oct 04 2018
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#L358tenRandomNumbers.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.
Oct 04 2018
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.
Oct 04 2018
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: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.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.
Oct 04 2018
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
Oct 04 2018
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: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/Qe7uHtAlas 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
Oct 05 2018
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: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.)On Friday, 5 October 2018 at 06:44:08 UTC, Nicholas Wilson 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/Qe7uHtAlas 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
Oct 05 2018
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:Note that this solution may do a lot of output and hence running of the destructor. Use: `.zip(repeat(&output))` to avoid that.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/Qe7uHtVery 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.)
Oct 05 2018