It's time for D to own up
Every time D is discussed with the general programming public,
eyebrows are raised by its garbage collector.
With many prospective users coming from “traditional” native languages like C
or C++, and with the inevitable comparisons between D and Rust
(which spends significant effort making lifetime management
a language-level feature), this is no big surprise.
Proponents of D are quick to fire back that it has the traditional malloc
and free
, which, when paired with RAII, let you to do anything C++ can.
The standard library even offers Unique
and RefCounted
types, reminiscent
of C++’s unique_ptr
and shared_ptr
.
However, a look at the code for these types tells a story of bit rot.
Their sad state gives the impression that this way of handling resources
(or at least memory) is of secondary importance to the almighty GC.
The Garbage Collector Cometh
So why garbage collection?
In the old days, there was malloc
and there was free
.
You got a slab of memory, you used it, and you released it.
Some will say there is some elegant simplicity here, but whatever else
can be said about manual memory management, it is hard.
Decades of errors — and very serious ones at that — have resulted from
common mistakes like using memory after you supposedly freed it.
So, for want of a less error-prone approach, garbage collection was born.
Let’s completely sidestep any performance arguments surrounding garbage collection and look at its semantics. The GC tells us a convenient lie — that memory we allocate will never go away. Since objects are only ever collected after our program has no references to them, and since we don’t know when the collector will run (possibly never for short-lived programs), the actual collection part of the GC is an implementation detail.
And now for something completely different
But memory is not the only resource. Programs use all sorts of other resources, like file handles, network sockets, and mutex locks. The GC model doesn’t work for these other things at all! You don’t want to hold them for an indeterminate amount of time. You want to be able to indicate when you are done with them so other parts of your program, or even other programs, can get their turn. So garbage-collected languages must offer some other mechanism for these other resources. D uses RAII for file handles and the like. Java has “try with resources”. C# has “using”. Python has “with”. And now you have to deal with two sets of rules and two mental models: one for memory, and one for everything else.
Own it!
An appealing alternative is ownership semantics. You specify an owner for a resource and its lifetime becomes tied to that of its owner. To do this without much pain, we want an “owning” type that can
-
Easily acquire a resource upon its construction.
-
Either prevent copies of itself from being made (signifying a single, unique owner) or maintain a reference count of how many copies of itself exist (allowing multiple owners at a cost of more complex semantics and implementation).
-
Allow ownership to be transferred from one owner to another.
-
Automatically release the resource when it itself is destroyed.
This ownership approach has trade-offs, but is arguably as simple to use as
garbage collection, works for every type of resource, and, as an added bonus,
actively encourages you to consider the lifetimes of resources in your code.
C++ finally supported requirement 3 with the addition of move semantics in 2011,
and the resulting unique_ptr
and shared_ptr
have been met with great acclaim.
Rust took things a step further and baked ownership into the language itself.
D is designed to be a versatile, general-purpose language.
Its overview on dlang.org describes it as a language
“[without] a VM, a religion, or an overriding philosophy”.
Surely, then, D and its standard library should offer good tools for those who
would prefer (often for very valid reasons) to use ownership semantics instead
of garbage collection.
To do my bit, I have started working
with the core devs to overhaul D’s owning types (Unique
and RefCounted
).
We have hit a few stumbling blocks, but good progress is being made and
everyone involved is quite enthusiastic about getting these types “right”.
With luck, ownership semantics will soon cease to be a second-class citizen
in the world of D.