Error: variable 'xyz' has scoped destruction, cannot build closure

Nicholas Wilson iamthewilsonator at hotmail.com
Fri Oct 5 06:22:57 UTC 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#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.



More information about the Digitalmars-d-learn mailing list