What is going on here?

Shachar Shemesh via Digitalmars-d digitalmars-d at puremagic.com
Sat Mar 7 23:02:44 PST 2015


On 07/03/15 23:44, w0rp wrote:
> Why not handle the scope(failure) cases in the constructor itself?
>
Because this breaks encapsulation.

struct SomeStruct {
	SomeOtherStruct a;
	MoreStruct b;

	this(int c) {
		this.a = SomeOtherStruct(c);
		this.b = MoreStruct(c+1);
	}
}

This code, as written, reasonable though it may seem, does not work with 
D's current implementation. If MoreStruct's constructor throws, a will 
be left with dangling resources. Or not. It really depends on how 
SomeOtherStruct is implemented.

Which is exactly my point. For things to be kept sane, each struct must 
be able to completely control its own resources. Once we accept that, 
however, a simple look at your example shows there is a much cleaner way 
of doing it:

> struct SomeStruct {
>      SmartPtr ptr;
>      SmartPtr ptr2;
>
>      this() {
>          ptr = malloc(...);
>          // No longer necessary: scope(failure) free(ptr);
>
>          /* Whatever follows, possibly throwing. */
>
>          ptr2 = malloc(...);
>          // No longer necessary: scope(failure) free(ptr2);
>      }
>
>      // No longer necessary: there is nothing left for us to destruct: ~this()
> }

This also avoids the bugs your implementation has, where you failed to 
override this(this) and opAssign, and thus you both are leaking some 
memory AND double freeing another. If SmartPtr is implemented correctly, 
the above, as is, is bug free.

Which is precisely why RAII are so great. They reduce the amount of code 
you need to write in order to be bug-free. You contain the tough corner 
cases in the RAII implementation, leaving everyone else to just write 
their code in peace.

Which is why I think problems such as this are a real setback.

Shachar


More information about the Digitalmars-d mailing list