On Tuesday, 13 December 2022 at 07:11:34 UTC, thebluepandabear wrote:
> Does this claim have merit? I am not far enough into learning D, so I haven't touched GC stuff yet, but I am curious what the D community has to say about this issue.
I disagree with the majority opinion on this subject. I find D's GC to often be heavily oversold, when it is particularly not applicable to many portions of my use case (game development). It certainly solves certain problems, but it introduces new ones. The emphasis behind the GC mentality seems to be that most(all!) people will never encounter those for their purposes and so they should literally just not think about it and trust the GC, until you suddenly can't anymore and the whole thing breaks apart. Alternative strategies do exist obviously, but they're often shoved into the backroom, with the salespeople only leading the customers to them after much grumbling and fumbling with their keys.
How do you instantiate a class object in D using the GC?
new Foo;
How do you instantiate one using malloc? Something like:
import core.memory;
import core.stdc.stdlib : malloc, free;
import core.lifetime : emplace;
T NEW(T, Args...)(auto ref Args args) /*@nogc (nope!)*/ if (is(T == class)) {
enum size = __traits(classInstanceSize, T);
void* mem = malloc(size);
scope(failure) free(mem);
//throw OOMError.get("Out of Memory in NEW!"~T.stringof); // wanna GC-allocate here? use a predefined object? or just ignore?
return mem !is null ? emplace!T(mem[0..size], args) : null;
}
// and don't forget
void FREE(T)(ref T obj) /*@nogc*/ if (is(T == class)) {
if (obj is null) return;
auto mem = cast(void*) obj;
//debug if (!GC.inFinalizer && GC.addrOf(mem)) return;
scope(exit) free(mem);
destroy(obj);
obj = null;
}
To people who are experienced with D, that's par for the course. And people who have already done a good deal of thinking about memory management in performance-intensive scenarios will understand the need to know their language's alternatives to begin with. But showing that to people coming to D as the alternative to what you're supposed to think is the right way everyone should use is just not attractive. It's a contradiction in one of D's core philosophies, IMO: "Solve basic problems and prevent easy bugs that most people walk into without thinking", which sounds like an admirable goal aimed at drawing in and protecting new users. Except then they're given a tool that will just create problems if used in the intended way (not thinking about it) if they get into certain domains of development. The explanation of "Well, obviously you need to think about it if you're going to be doing THAT!" just doesn't mesh with the way it's initially sold.
Pipe dream: Why not new malloc Foo;
? (or "deterministic" or something. and then, new rc Foo;
!) What if, to prevent accidental intermingling, it were a storage class? malloc Foo mfoo = new Foo; // Error!
. Just thinking out loud. Part of this can already be done by wrapping everything in structs and templates. But just more noise!
That said, regarding your specific question, there are numerous parts of the D standard library you can safely use without allocating with the GC (and I do, and still love it) and non-allocating alternatives are often added (e.g. .join
vs .joiner
). Though the problem exists many components can't be explicitly @nogc
(a caveat I find it just not worth it to worry about anymore- it takes up too much time and effort that could be better spent on the code itself than on solving a trillion compiler errors trying to satisfy every possible aspect and edge case of @nogc-dom).
There is a lot of code you can write in D that, without going over the stdlib with a fine-toothed comb, you can be reasonably sure will probably never GC-allocate, even if it's not explicitly @nogc, if that's an acceptable tradeoff for the code safety requirements in your use case. std.container.array is good, as previously mentioned. You can build on this and make malloc/ref-counted variations of hashmaps/associative arrays too (if you want to spend the effort on it). I believe there are some third-party libraries up on dub for that already. The operator overloading and syntactic sugar is good enough that everything can look "just like" native D runtime/GC constructs if you want, except for some declarations (but all that is not exactly "out of the box", if we're still thinking of the prospective new user context here).
tl;dr: I don't hate the GC. It's great for one&dones. I just wish it wasn't so heavily lauded as The Truth & The Way.