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