100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

D Interview Questions

The concepts D interviewers actually probe: struct vs class semantics, the function attribute system, template constraints, and the actor-model concurrency of std.concurrency.

PracticeIntermediate11 min readJul 10, 2026
Analogies

What D Interviews Actually Test

Because most candidates already know general programming concepts, D interviews tend to focus on the handful of areas where D genuinely diverges from mainstream languages: the value-vs-reference split between struct and class, the compiler-enforced function attribute system (@safe, @nogc, pure, nothrow), how template constraints resolve overloads at compile time, and the share-nothing, message-passing concurrency model in std.concurrency. Expect questions that ask you to trace exactly what the compiler does, not just recite a definition.

🏏

Cricket analogy: Like a selector testing a young batter specifically on footwork against spin rather than pace everyone already handles the same way, interviewers probe the aspects of D that differ most from mainstream languages.

Structs vs Classes: Value vs Reference Semantics

A struct in D is a value type: assignment copies its bytes, it can live on the stack, and it supports no inheritance, though composition via alias this or mixin templates covers most of what you'd otherwise reach for inheritance to do. A class is always a reference type — new allocates it on the GC heap by default — supports single inheritance plus multiple interface implementation, and dispatches virtual calls through a vtable. The classic interview answer to 'when do you use a struct over a class' is: prefer struct for small, copyable value types like Point or Color where identity doesn't matter and immutability is natural, and class when you need polymorphism, shared reference identity, or nullable object semantics.

🏏

Cricket analogy: Like comparing a fielding substitute brought on for one over — a copy with no lasting bond to the squad — to a permanent squad member tracked as one identity across the tournament, a D struct is copied by value while a class is a shared reference.

The Function Attribute System: @safe, @nogc, pure, nothrow

D treats memory safety and side-effect discipline as compiler-checked properties, not documentation conventions: @safe forbids pointer arithmetic, unchecked casts, and calls into @system code inside the function; @nogc forbids GC allocation; nothrow guarantees no exception escapes; and pure guarantees the return value depends only on the arguments and doesn't read or write mutable global state. A strongly pure function — one whose parameters are all unique or immutable — can have its freshly allocated return value implicitly converted to immutable, because the compiler can prove no other reference to that memory exists, which is a favorite 'why does this compile' interview trap.

🏏

Cricket analogy: Like DRS enforcing a strict rule set automatically rather than trusting memory, D's pure/nothrow/@nogc/@safe attributes are compiler-enforced guarantees rather than comments a reviewer hopes are true.

A common D interview question: 'Why can a strongly pure function's return value be assigned to an immutable variable without an explicit cast?' Because a strongly pure function takes only immutable or uniquely-referenced (not aliased elsewhere) parameters, the compiler can prove the freshly returned memory has no other live reference, so converting it to immutable is provably safe rather than a language special case.

Template Constraints and Overload Resolution

D template constraints, written as if (isNumeric!T) or checked with an is() expression, don't produce a compile error the instant they fail — instead, a template whose constraint isn't satisfied is silently removed from the set of candidate overloads, exactly like SFINAE in C++ but expressed as ordinary boolean logic instead of ad hoc trick expressions. A frequent interview exercise is tracing why a call resolves to a particular overload among several templated and non-templated candidates, which tests whether you understand that constraint failure is exclusion, not a hard error, until no viable candidate remains.

🏏

Cricket analogy: Like a franchise scouting system silently filtering out bowlers below a pace threshold rather than rejecting the whole squad, D's template constraints silently exclude a non-matching overload rather than erroring across the whole file.

Concurrency: The Actor Model in std.concurrency

std.concurrency implements a share-nothing, message-passing model inspired by Erlang: spawn(&worker, args) starts a new logical thread with its own Tid mailbox, send(tid, message) enqueues a message, and receive((MessageType m) { ... }) pattern-matches on the mailbox's contents. Crucially, D does not let two threads implicitly share mutable data — a variable must be explicitly marked shared or immutable to cross a thread boundary, and the compiler rejects code that tries to pass ordinary mutable data by reference into spawn, catching a whole class of data races at compile time instead of leaving it to runtime debugging.

🏏

Cricket analogy: Like each fielding position having its own dedicated radio channel to the captain rather than everyone shouting on one shared frequency, spawn/send/receive gives each thread its own mailbox instead of shared mutable memory.

d
import std.concurrency;
import std.stdio;

void worker(Tid owner)
{
    receive(
        (int n) {
            int result = n * n;
            send(owner, result);
        }
    );
}

void main()
{
    auto tid = spawn(&worker, thisTid);
    send(tid, 7);

    receive(
        (int result) {
            writeln("Result from worker: ", result);
        }
    );
}

The shared type qualifier in D is not a mutex — marking a variable shared only tells the compiler two threads may touch it and disables some unsafe implicit operations; it does not make reads and writes atomic or race-free. You still need synchronized blocks, core.atomic functions, or a std.concurrency message-passing design to actually prevent data races. Interviewers often ask this specifically because it's a common misconception coming from languages where 'shared' implies built-in locking.

  • D interviews focus on the language's distinctive areas: value vs reference semantics, compiler-enforced attributes, template constraint resolution, and message-passing concurrency.
  • struct is a value type (copied on assignment, no inheritance); class is always a GC-heap reference type supporting inheritance, interfaces, and virtual dispatch.
  • @safe, @nogc, pure, and nothrow are compiler-verified guarantees, not documentation — the compiler rejects code that violates them.
  • A strongly pure function's freshly allocated return value can implicitly become immutable because the compiler can prove no other reference exists.
  • Failed template constraints silently exclude that overload from resolution rather than raising an immediate hard error, mirroring C++ SFINAE with plain boolean syntax.
  • std.concurrency's spawn/send/receive implements Erlang-style share-nothing concurrency; data must be explicitly shared or immutable to cross a thread boundary.
  • shared in D is not a mutex — it only permits cross-thread visibility; you still need synchronized or core.atomic for actual race safety.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#DProgrammingStudyNotes#DInterviewQuestions#Interview#Questions#Interviews#Actually#StudyNotes#SkillVeris#ExamPrep