Comment #0 by default_357-line — 2018-07-06T08:25:36Z
Consider this code:
struct S
{
@disable this();
bool b = false;
this(int) { b = true; }
invariant { assert(b == true); }
@safe ~this() { }
}
@safe void main()
{
S s = S.init;
}
I believe the appropriate response to this is that invariant checking must be disabled on struct destructors. Struct destructors are supposed to be able to handle T.init anyways; otherwise the behavior of moveEmplace is nonsensical. This problem is, as far as I can tell, completely solved by requiring structs to be either invariant-valid or T.init at destructor call.
Comment #1 by bugzilla — 2018-07-08T02:36:08Z
I think this change defeats the purpose of having an invariant. T.init is supposed to be a valid object, and so should pass the invariant.
It's not a bug, it's the way the language is designed to work.
To make this modification to the semantics, a fairly compelling use case will be required.
Comment #2 by default_357-line — 2018-07-08T03:01:43Z
T.init having to be a valid object makes structs unusable for value types that contain objects by reference that must not be null, or arrays that must not be empty. At my workplace, this scenario turns up a *lot* when trying to use structs to contain domain data.
Comment #3 by default_357-line — 2018-07-08T03:16:37Z
That said, the only case "invariant on destruction" even comes up, is when changing values via public fields and using the destructor as a kind of "last-ditch invariant check", which imo is largely useless because one by definition you haven't called domain methods on it anyway, and two - if T.init is supposed to be valid, you have to recheck all references for null in the destructor *anyways*, since you can't have the checks in the invariant.
The only thing that having to have T.null be a valid object buys you is making it impossible to use invariant for null checks. As far as I can see, the userland code ends up strictly, necessarily more awkward by that rule.
Comment #4 by ali.akhtarzada — 2018-07-12T22:30:55Z
(In reply to Walter Bright from comment #1)
> I think this change defeats the purpose of having an invariant. T.init is
> supposed to be a valid object, and so should pass the invariant.
>
> It's not a bug, it's the way the language is designed to work.
>
> To make this modification to the semantics, a fairly compelling use case
> will be required.
Does this mean the compile time constructed value of T must be a valid runtime constructed value of T to be able to use invariants?
Comment #5 by default_357-line — 2018-07-23T08:45:36Z
Yep.
Comment #6 by robert.schadek — 2024-12-13T18:59:31Z