Using a delegate stored as a member of a destroyed struct?

Steven Schveighoffer schveiguy at yahoo.com
Sun Jan 26 18:27:08 PST 2014


On Sun, 26 Jan 2014 18:41:00 -0500, Nicolas Sicard <dransic at gmail.com>  
wrote:

> Running a piece of code that can be reduced to:
>
> ---
> import std.stdio;
>
> void main()
> {
> 	import std.range;
> 	foreach(item; iota(0, 10).transform(2))
> 		writeln(item);
> }
>
> auto transform(T)(T list, real x)
> {
> 	auto t = /* new */ Transformer(x);   // line 12
> 	return t.applyTo(list);
> }
>
> struct Transformer
> {
> 	real delegate(real) fun;
>
> 	this(real x)
> 	{
> 		fun = (real r) => r * x;
> 	}
>
> 	auto applyTo(T)(T list)
> 	{
> 		import std.algorithm;
> 		return list.map!(x => fun(x));
> 	}
> }
> ---
>
> the program segfaults. I guess it's because fun is destroyed when 't'  
> goes out of scope in 'transform'. I would have thought that the  
> MapResult struct returned by 'applyTo' still holds a valid copy of fun,  
> but I'm wrong... Is there a way to do it?

No. Just don't do that. The runtime is permitted to move structs  
bit-for-bit, so you are not allowed to store a pointer that references  
'this'. Unless you prevent copying via @disable this(this), your struct  
could be moved if someone, for instance, passed it as a parameter on the  
stack, or returned it.

A delegate using 'this' as the context pointer is the same thing.

The only way to solve this is to put the struct on the heap. But why even  
use a struct? You could just use a closure (which automatically goes on  
the heap if needed):

auto Transformer(real x)
{
   return (real r) => r * x;
}

auto applyTo(X, T)(X fun, T list)
{
    import std.algorithm;
    return list.map!(x => fun(x));
}

auto transform(T)(T list, real x)
{
    auto t = Transformer(x);
    return t.applyTo(list);
}

-Steve


More information about the Digitalmars-d-learn mailing list