English for D Language Developers
Vocabulary for developers working in D — compile-time function execution, the garbage collector opt-out, templates versus generics, and range-based algorithm talk for systems teams.
D sits deliberately between C++ and higher-level languages, and describing it precisely means being exact about which features are compile-time, which are optional, and which are “D’s version of” a concept from another language rather than an identical import of it.
Key Vocabulary
Compile-time function execution (CTFE) — D’s ability to run ordinary D functions during compilation to produce constants, lookup tables, or validated values, without a separate macro or template metaprogramming language. “We don’t need a code generator for this table — compile-time function execution runs the same function during the build, so the lookup table gets baked into the binary instead of computed on every startup.”
Template — D’s mechanism for writing generic, type-parameterized code that gets instantiated per concrete type at compile time, similar in spirit to C++ templates but with cleaner syntax and built-in constraints. “This function is a template constrained to numeric types, so calling it with a string won’t even compile — the constraint documents the requirement and enforces it at the same time.”
@nogc / garbage-collector opt-out — an annotation marking a function as guaranteed not to allocate on D’s garbage-collected heap, letting performance-critical code opt out of GC pauses entirely while the rest of the codebase still uses it freely.
“Mark the render loop @nogc — we can’t tolerate a GC pause mid-frame, but the asset-loading code outside the loop is fine using the GC normally.”
Range — D’s standard abstraction for a sequence that can be iterated lazily, analogous to an iterator but composable through UFCS (uniform function call syntax), forming the backbone of most of D’s standard algorithm library.
“Chain these as ranges instead of allocating an intermediate array at each step — filter, map, and take all compose lazily here, so nothing gets materialized until the final range actually gets consumed.”
Contract programming (in/out blocks) — D’s built-in support for preconditions and postconditions attached directly to function declarations, checked automatically in debug builds and stripped in release builds.
“Put that non-null check in an in contract block rather than as a manual if at the top of the function — it documents the precondition explicitly, and it’s automatically compiled out in release builds where we don’t want the overhead.”
Common Phrases
- “Can this run at compile time via CTFE, or does it genuinely need runtime data?”
- “Is this a template, or would a plain function with overloads be simpler here?”
- “Does this path need to be @nogc, or is GC allocation acceptable here?”
- “Are we composing this as a range, or materializing an array we don’t actually need?”
- “Should that precondition be an in-contract, or a manual runtime check that ships to production?”
Example Sentences
Explaining a build-time optimization: “This constant table isn’t hardcoded — it’s generated by compile-time function execution from the source data, so if the source data changes, the table regenerates automatically on the next build with zero runtime cost.”
Describing a generics decision:
“We wrote this as a template constrained to types implementing the Comparable interface, so the compiler rejects invalid instantiations immediately instead of failing somewhere deep inside the sort algorithm.”
Justifying a GC-related change: “We marked the hot path @nogc after profiling showed GC pauses were causing frame drops — everything outside that path still allocates normally, so this is a targeted opt-out, not a rewrite.”
Professional Tips
- Reach for compile-time function execution whenever a value can legitimately be computed ahead of time — it eliminates runtime cost entirely, and D makes this far less ceremonious than a separate build-step code generator.
- Use a template with explicit constraints rather than an unconstrained one — an unconstrained template produces confusing compiler errors deep inside its body instead of a clear error at the call site.
- Apply @nogc narrowly, to the specific paths that actually can’t tolerate GC pauses — marking too much code @nogc just fights the language instead of using its GC where it’s genuinely fine.
- Prefer composing algorithms as ranges over allocating intermediate arrays at each step — it’s both more idiomatic D and avoids unnecessary allocations in hot paths.
- Use contract programming (in/out blocks) for preconditions and postconditions instead of manual checks scattered through the function body — contracts are self-documenting and automatically stripped from release builds.
Practice Exercise
- Explain what compile-time function execution does and why it saves runtime cost.
- Describe when you’d mark a function @nogc versus leaving it using the garbage collector.
- Write a sentence explaining why composing ranges lazily can be more efficient than allocating arrays at each step.