Class destructors - clarify what is safe

H. S. Teoh hsteoh at qfbox.info
Tue Feb 17 23:52:25 UTC 2026


On Tue, Feb 17, 2026 at 01:52:57PM -0800, Ali Çehreli via Digitalmars-d-learn wrote:
> On 2/14/26 4:46 PM, H. S. Teoh wrote:
> > On Sat, Feb 14, 2026 at 10:03:17PM +0000, Paul Backus via Digitalmars-d-learn wrote:
> > > On Saturday, 14 February 2026 at 18:42:21 UTC, H. S. Teoh wrote:
[...]
> > > > Calling .destroy on a class object more than once may trigger UB.
> > > 
> > > This is technically true, in the sense that calling any function
> > > could conceivably trigger UB, but destructors *should* be
> > > idempotent, and any destructor marked as @safe *must* be
> > > idempotent.
> > 
> > True.  So any dtor that closes a file should set its handle to a
> > null value so that any subsequent calls will be a no-op.
[...]
> The way I've learned to see it for decades now, initially from the
> point of view of C++, calling the destructor more than once should be
> a mistake.
> 
> The reason is, when the destructor completes, the object is considered
> to be dead. Calling any non-static function on it should be considered
> a programming error.

I agree with this, in principle.

Now, idempotent operations can also be helpful in some situations.  It
lets you elide verbose checks (like pervasive null checks peppering the
code) by letting the caller ignore the exact state of an object because
it's harmless (and does not change the result) to perform the operation
twice.

Idempotent destruction is questionable, however. If a dtor is called
twice, then something smells bad, even if it's technically not wrong.
It's the same as if a ctor were called twice to construct an object.
The whole point of a ctor is to start from an invalid initial state and
initialize the object into a valid state.  Once that has been done, the
object is already in a valid state and should not be initialized again
-- the ctor code was written with the assumption that it wasn't in a
valid state to start with. Violating this breaks the assumption, and
this can lead to nasty bugs.  Similarly, a dtor is written under the
assumption that the object is in a valid state.  After a dtor is
finished the object is in an *invalid* state; calling it again breaks
this assumption, which means it's liable to lead to nasty bugs.  If
druntime causes a ctor to run twice under any circumstances, I'd
consider it a bug. Similarly, if a dtor were to run twice on the same
object, I'd consider that a bug.


T

-- 
Roman numerals are difficult; but when you get to 159, it just CLIX.


More information about the Digitalmars-d-learn mailing list