T.init, struct destructors and invariants - should they be called?
Stanislav Blinov
stanislav.blinov at gmail.com
Sun Nov 18 15:45:51 UTC 2018
On Sunday, 18 November 2018 at 15:15:11 UTC, FeepingCreature
wrote:
> On Sunday, 18 November 2018 at 14:54:05 UTC, Stanislav Blinov
> wrote:
>> On Sunday, 18 November 2018 at 14:47:51 UTC, FeepingCreature
>> wrote:
>>> On Sunday, 18 November 2018 at 14:38:09 UTC, Stanislav Blinov
>>> wrote:
>>>> @safe unittest {
>>>> Nullable!S a; // Look ma, no assert!
>>>> }
>>>
>>> a = S(new Object); // Look pa, assert!
>>
>> That has to do with poor implementation of that example
>> Nullable, not the union. opAssign should check for _hasValue.
>
> Right, which means you end up with moveEmplace in opAssign,
No you don't :) (I know, I know, I'm such a negative personality):
// still rudimentary, no checks for hasElaborateDestructor
struct Nullable(T) {
this(T val) { value = val; }
this(typeof(null) val) {}
~this() { cleanup(); }
@property @trusted
ref T value() {
assert(_hasValue);
return _u.value;
}
@property @trusted
void value(T val) {
// when it's rebinding, there's no need to move or
moveEmplace,
// just overwrite the union
if (_hasValue) {
// this, or it could actually assign to _u.value,
depending on the desired semantics of Nullable
destroy(_u.value);
_u = U(val);
} else {
_u = U(val);
_hasValue = true;
}
}
@property
void value(typeof(null)) { cleanup(); }
void opAssign(T val) {
// arguably this should just duplicate what `value` does,
// to avoid unnecessary copies passed around.
value = val;
}
void opAssign(typeof(null) val) { value = val; }
private:
union U { T value = T.init; }
U _u;
@property ref _payload() inout { return _u.value; }
bool _hasValue;
void cleanup() {
if (!_hasValue) return;
destroy(_u.value);
_hasValue = false;
}
}
> which is the current Nullable implementation.
Looking at that implementation, ouch... Maybe I'm missing
something?..
> Which only works because union{} essentially functions as a
> semi-official backdoor in the typesystem, even in @safe. Is
> that *really* good language design, though?
That? Yes. Unions are actually useful now, unlike what they were
before.
Anyway, my point is that unions *are* the tool for that
particular job, just like you said initially. Which has little to
do with the actual topic :)
For example, I'd argue that the *actual* implementation *must*
`move` it's passed-by-value argument (regardless of what it uses
for storage), because the caller already made a required copy.
But that means wiping out the argument back to T.init, and then
we're back to square one.
More information about the Digitalmars-d
mailing list