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