What -betterC Removes and Why
-betterC compiles D without the full D runtime, druntime: no garbage collector, no full exception unwinding across arbitrary throw sites, no TypeInfo-driven features like growable dynamic arrays or built-in associative arrays that need runtime support, and no automatic module constructors -- leaving a language that's still far more expressive than C, with templates, contracts, and RAII via struct destructors, but that links like a tiny C program with none of the runtime baggage, making it viable for microcontrollers, kernels, and any environment where you can't assume a GC or exception unwinder exists.
Cricket analogy: T20 cricket strips away the extra overs, extended field-restriction phases, and rest days of a Test match while keeping the core rules of batting and bowling intact; -betterC strips away the GC and full runtime while keeping D's core language features like templates and RAII intact.
What You Can and Can't Use Under -betterC
Under -betterC you can still use templates, structs with destructors for deterministic RAII, bounds-checked slices, and contracts compiled as plain asserts -- but you lose the GC entirely, so no dynamic arrays that grow via ~= and no built-in associative arrays unless you bring a GC-free alternative, no full try/catch/finally across arbitrary throw sites without extra scaffolding, and no module static constructors running automatically; memory must be managed manually via core.stdc.stdlib's malloc/free or via RAII structs with explicit destructors.
Cricket analogy: A franchise's overseas replacement player can still bat, bowl, and field exactly like everyone else, but doesn't get the same central contract retainer or accrued domestic pension the full squad gets; -betterC code still gets templates, structs, and RAII, but doesn't get the GC's automatic memory management.
// betterc_ring_buffer.d
import core.stdc.stdlib : malloc, free;
struct RingBuffer(T) {
private T* data;
private size_t capacity;
private size_t head, tail, count;
this(size_t cap) {
capacity = cap;
data = cast(T*) malloc(T.sizeof * cap);
}
~this() {
if (data) free(data);
}
void push(T item)
in (count < capacity, "RingBuffer full")
{
data[tail] = item;
tail = (tail + 1) % capacity;
count++;
}
}
extern(C) void main() {
auto buf = RingBuffer!int(16);
buf.push(42);
}
// Build with: dmd -betterC betterc_ring_buffer.dPractical Use Cases: Embedded and Kernel Code
-betterC targets microcontrollers, such as ARM Cortex-M via ldc2's cross-compilation, OS kernel modules, and any Linux or Windows driver-style code, and it's also a common bridge for calling D libraries from C++, Rust, or other GC-free ecosystems where linking druntime would be unacceptable bloat or would conflict with the host's own memory management; a well-known pattern is writing performance-critical, allocation-free hot paths in -betterC D and linking them into a larger regular-D or C application.
Cricket analogy: A specialist death-overs bowler is brought on only for the specific high-pressure final overs where their exact skill set matters most, not the whole innings; -betterC D is brought in only for the specific allocation-free, no-runtime hot path where its exact constraints matter, not the whole application.
ldc2, the LLVM-based D compiler, is the compiler of choice for embedded and cross-compiled -betterC work because it targets ARM, RISC-V, and other embedded architectures via LLVM's backend, while dmd's -betterC support is primarily aimed at x86/x86_64 desktop and server systems programming.
Under -betterC, throwing an exception with 'throw' still compiles, but there is no unwinder to catch it across most contexts without extra runtime support -- an uncaught throw in -betterC typically calls a trap or abort rather than propagating cleanly. Treat -betterC code as effectively exception-free and use return codes, out-parameters, or Result-style structs for error handling instead.
- -betterC removes druntime -- no GC, no full exception unwinding, no automatic module constructors -- for a C-sized runtime footprint.
- Templates, structs with destructors for RAII, contracts-as-asserts, and D's type system all still work under -betterC.
- Dynamic arrays that grow via ~= and built-in associative arrays need GC support and generally aren't usable under -betterC.
- Manual memory management via core.stdc.stdlib malloc/free, or RAII struct destructors, replaces the GC for -betterC code.
- ldc2 is the typical choice for cross-compiling -betterC D to embedded targets like ARM Cortex-M via LLVM.
- Uncaught throw in -betterC code generally aborts rather than unwinding cleanly -- treat it as effectively exception-free.
- A common pattern is isolating just the allocation-free hot path in -betterC and linking it into a normal GC-enabled D or C application.
Practice what you learned
1. What is the primary effect of compiling with -betterC?
2. Which D features remain usable under -betterC?
3. What typically happens if an uncaught exception is thrown in -betterC code?
4. Why is ldc2 typically preferred over dmd for embedded -betterC targets?
5. How should memory typically be managed in -betterC code?
Was this page helpful?
You May Also Like
D for High-Performance Computing
D combines low-level control -- @nogc code, SIMD intrinsics, and manual memory layout -- with high-level expressiveness, making it a practical choice for numerically intensive, performance-critical workloads.
D and C Interop
How D's native extern(C) linkage lets you call existing C libraries directly and expose D functions to C code without writing wrapper glue.
Testing D Code
D has unit testing built directly into the language with unittest blocks, plus contract programming (in/out/invariant) and assert for runtime correctness checks.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics