May 18, 2026
The D Language: A Better C/C++ Alternative Only a Few Programmers Know
A modern, fast, fully-featured, compiled language that you can use beyond systems programming

By Shalitha Suranga
9 min read
There are numerous programming languages in the world, but only a few languages dominate the modern software industry. Being only a good programming language isn't enough to become a competitive, popular language — programming languages need corporate funding, stable growth, a good marketing strategy, and tooling to become popular. There are good programming languages that aren't so popular. For example, in my previous articles, I've discussed Lua and Tcl, lesser-known languages; they are undoubtedly good, but unfortunately not very popular.
D, also known as dlang, is another lesser-known but highly usable language. It is an open-source, compiled, multi-paradigm systems programming language like C and C++, but with modern built-in features and a fully featured cross-platform standard library. The D language has C-like but modern syntax without C source-code-level compatibility— it improved C in a different direction than C++.
Let's discuss why the D language is special, the reasons behind its popularity issues, highlighted features, how to write code with D, and what you can build with it.
What is D, and why is it special?
D is a compiled, statically-typed, multi-paradigm language for systems programming. Walter Bright initiated the D language project and released the first D compiler in 2001. D was initially developed to create a new language by re-engineering C++. However, the project later evolved into a modern, fully-featured, general-purpose language that programmers use beyond systems programming with a fully-featured, cross-platform standard library:
D specially allows you to,
- Directly use C code within D projects by embedding its own C compiler implementation into the D compiler
- Easily create a C++ interop by linking C++ object files
- Compile D sources without linking the D standard library for lightweight development needs
D offers a separate C/C++ alternative language while also offering better interoperability with legacy C/C++ code.
Why is the D language not so popular?
D is undoubtedly a Zig-like modern systems programming language that offers a C-like syntax with most modern feature extensions that C++ offers. It has a clever C/C++ interop mechanism and features for modern systems programming to compete with all popular better-C and better-C++ languages like Rust, Go, and Zig, but it's not as popular as others. Why?
Here are the true reasons behind D's lack of popularity:
- Released when a better C++ language isn't needed: The first version of D was released in 2001, when programmers already had stable C++ codebases, and C++ wasn't as complicated as today, so they didn't want an alternative language to solve C++'s issues
- Built-in mandatory garbage collection: Initial D compiler versions had garbage collection as a core feature, and D released a way to opt-out from garbage collection in later releases, so D couldn't initially satisfy low-level, lightweight programming needs
- Standard library clash: D had two incompatible standard libraries initially: the official Phobos and community-backed Tango. The existence of two standard libraries confused early adopters
- No corporate backing: D didn't have a large tech company behind it. This kept core language goals unbiased, but created popularity issues and a small ecosystem around the language
Highlighted features of D
These are the reasons why you should learn D during your programming career and try to build at least one project with it:
- Fully-featured, well-organized standard library: D has a more organized, complete standard library compared to C++. Built-in JSON handling, cross-platform file system API, child process API, compile-time reflection, native testing support, and high-level concurrency features are highlights
- You choose low-level, mid-level, or high-level: You can write unsafe (memory) low-level code as C or safe garbage-collected code as Go. Or mid-level code with basic memory safety annotations. You can also use an inline assembler to directly write CPU-specific machine code. You decide the abstraction level — not the language
- Better C/C++ interop: The D compiler can compile C too, so you can seamlessly include C source files within D codebases. D can't natively compile C++, but it lets you seamlessly include C++ sources by offering ABI-level mapping, basically passing C++ object files to the D compiler and using C++ bindings
- Flexible, general-purpose systems programming: You can create libraries, frameworks, or any software product using the standard D runtime. Or you can build lightweight embedded systems or operating systems without linking the standard D runtime
- Modern high-level syntax with low-level control: D doesn't use a preprocessor to import files — it offers modern Java-like namespaced imports, along with other modern language features that other better-C/C++ languages offer, but gives you full control as C does
Getting started with D programming
Let's learn D in five minutes, covering installation, built-in features, standard library basics, and C/C++ interop to practically understand D's potential as a modern systems programming language:
Installing the D compiler
There are several D compilers out there. I prefer the official reference compiler DMD (Digital Mars D). Download the DMD compiler for your operating system from the official downloads page.
I installed the latest D compiler from the Debian installer package. Make sure the compiler binary works after installation by entering dmd on terminal:
Writing a basic D program
Let's write, compile, and run a very simple D program to get started:
import std.stdio;
void main() {
writeln("Hello dlang");
}import std.stdio;
void main() {
writeln("Hello dlang");
}D uses Java-style package imports, and here we imported stdio module from the standard library to use writeln for printing a string.
Add the above code to main.d and compile using dmd:
dmd main.ddmd main.dThe compiler will create the main binary executable and you can run it to see the output:
Fundamental data types
There are 21 fundamental data types in D for storing booleans, numbers, imaginary numbers, complex numbers, and characters.
Here is a demonstration of some frequently-used ones:
int a = -10;
uint b = 100;
float c = -2.127f;
ubyte d = 255;
char e = 'a';
dchar f = '🚀';
bool g = true;
writeln("a = ", a);
writeln("b = ", b);
writeln("c = ", c);
writeln("d = ", d);
writeln("e = char:", e);
writeln("f = ", f);
writeln("g = ", g);int a = -10;
uint b = 100;
float c = -2.127f;
ubyte d = 255;
char e = 'a';
dchar f = '🚀';
bool g = true;
writeln("a = ", a);
writeln("b = ", b);
writeln("c = ", c);
writeln("d = ", d);
writeln("e = char:", e);
writeln("f = ", f);
writeln("g = ", g);
As you noticed, data type keywords are similar to C/C++, but are simpler since they don't use separate keywords to construct signed/unsigned types, e.g., C/C++'s unsigned long is ulong in D.
D lets you use get important variable details like size, minimum limit, maximum limit, etc. via type properties, as shown in the following code snippet:
ubyte a = 100;
writeln("a.sizeof = ", a.sizeof, " byte(s)"); // a.sizeof = 1 byte(s)
writeln("a.min = ", a.min); // a.min = 0
writeln("a.max = ", a.max); // a.max = 255ubyte a = 100;
writeln("a.sizeof = ", a.sizeof, " byte(s)"); // a.sizeof = 1 byte(s)
writeln("a.min = ", a.min); // a.min = 0
writeln("a.max = ", a.max); // a.max = 255Complex data types and built-in structures
D supports pointers, strings, arrays, maps, structs, classes, enums, and interfaces.
You can use strings similar to C++, but D uses tilde (~) for string concatenation
string s = "Hello" ~ " " ~ "D";
writeln(s); // Hello Dstring s = "Hello" ~ " " ~ "D";
writeln(s); // Hello DHere is how you can work with arrays and maps:
int[] a = [1, 100, 200, 500];
a[0] = 120;
int[string] scores;
scores["John"] = 50;
scores["Ann"] = 52;
writeln("a = ", a); // a[] = [1, 100, 200, 500]
writeln("scores = ", scores); // scores = ["Ann":52, "John":50]int[] a = [1, 100, 200, 500];
a[0] = 120;
int[string] scores;
scores["John"] = 50;
scores["Ann"] = 52;
writeln("a = ", a); // a[] = [1, 100, 200, 500]
writeln("scores = ", scores); // scores = ["Ann":52, "John":50]D supports Zig-like array slicing. Look at the following example:
int[] a = [1, 2, 3, 4, 5, 6];
auto b = a[2..4];
writeln("b = ", b); // b = [2, 3]int[] a = [1, 2, 3, 4, 5, 6];
auto b = a[2..4];
writeln("b = ", b); // b = [2, 3]D structs and enums are very similar to C++, but D classes are simpler than C++'s; OOP with D creates cleaner source files.
Functions and control structures
D functions are similar to C++ functions, but they additionally support named parameters, nested functions, and use the ref keyword for syntactically-better pass-by-reference:
import std.stdio;
void main() {
int add(int a, int b) {
return a + b;
}
void modifyme(ref int c) {
c = 200;
}
writeln("10 + 5 = ", add(10, 5));
writeln("20 + 10 = ", add(a: 20, b: 10));
int c;
modifyme(c);
writeln("c = ", c);
}import std.stdio;
void main() {
int add(int a, int b) {
return a + b;
}
void modifyme(ref int c) {
c = 200;
}
writeln("10 + 5 = ", add(10, 5));
writeln("20 + 10 = ", add(a: 20, b: 10));
int c;
modifyme(c);
writeln("c = ", c);
}
D implements all standard control statements, including if, while, do, switch, for, try, etc. D additionally offers foreach_reverse, and in keyword, final switch for enum checkup to improve coding productivity:
int[] a = [100, 200, 500];
foreach(n; a) {
write(n, " ");
}
writeln();
foreach_reverse(n; a) {
write(n, " ");
}
writeln();
int[string] c;
c["x"] = 10;
c["y"] = 20;
if("x" in c) {
writeln("c[x] = ", c["x"]);
}int[] a = [100, 200, 500];
foreach(n; a) {
write(n, " ");
}
writeln();
foreach_reverse(n; a) {
write(n, " ");
}
writeln();
int[string] c;
c["x"] = 10;
c["y"] = 20;
if("x" in c) {
writeln("c[x] = ", c["x"]);
}
Using the D standard library (Phobos)
In previous code examples, we've already used the standard I/O module from the D standard library to output things. The fully featured D standard library offers common algorithms, advanced data structures, file-handling features, child-process API, math functions, parallel programming API, string-handling functions, reflection features, and more.
All standard library modules are in the std namespace and can be listed as follows on Linux:
ls /usr/include/dmd/phobos/stdls /usr/include/dmd/phobos/std
The D standard library is more organized and feature-rich than C++'s. For example, in C/C++, you'll have to use a third-party child process library, but D offers process handling features via the standard library:
import std.stdio;
import std.process;
void main() {
auto r = execute(["node", "--version"]);
writeln("status = ", r.status);
writeln("output = ", r.output);
}import std.stdio;
import std.process;
void main() {
auto r = execute(["node", "--version"]);
writeln("status = ", r.status);
writeln("output = ", r.output);
}The child process API even provides a separate function to execute commands in the shell:
auto r = executeShell("echo $SHELL && pwd");
writeln("status = ", r.status);
writeln("output = ", r.output);auto r = executeShell("echo $SHELL && pwd");
writeln("status = ", r.status);
writeln("output = ", r.output);See the official standard library reference to browse all standard library modules.
Integrating C code
You can directly call C functions from D by compiling your C source files along with your D source files. Here is a simple demonstration:
Create cmodule.c and write a simple function:
int add(int a, int b) {
return a + b;
}int add(int a, int b) {
return a + b;
}Call this function directly from main.d by importing cmodule :
import std.stdio;
import cmodule;
void main() {
writeln("add(10, 20) = ", add(10, 20));
}import std.stdio;
import cmodule;
void main() {
writeln("add(10, 20) = ", add(10, 20));
}Now compile both source files using dmd:
dmd main.d cmodule.cdmd main.d cmodule.cRun the program. It'll work as expected:
Integrating C++ code
You have to create C++ bindings using extern (C++) to call C++ from D, but you can seamlessly include C++ source files in the compilation steps. Here is a simple demonstration:
First, create cppmodule.cpp and add a simple function:
int add(int a, int b) {
return a + b;
}int add(int a, int b) {
return a + b;
}Create bindings for the add() function and call it from main.d, as follows:
import std.stdio;
extern (C++) int add(int a, int b);
void main() {
writeln("add(10, 20) = ", add(10, 20));
}import std.stdio;
extern (C++) int add(int a, int b);
void main() {
writeln("add(10, 20) = ", add(10, 20));
}Compile the C++ module into an object file. Next, include the generated object file in dmd compilation process:
g++ -c cppmodule.cpp
dmd main.d cppmodule.og++ -c cppmodule.cpp
dmd main.d cppmodule.oThe program will work as expected:
What you can build with D
D is a modern systems programming language with many built-in general-purpose programming features, so it's suitable for scenarios you consider using C/C++, Rust, Zig, or Go:
- Operating systems and embedded software: D can be compiled without including the D runtime, so it can be used to build fast, lightweight operating systems and embedded systems
- CLI programs: Cross-platform standard library features offer productive standard I/O, file, and process handling features for building CLI utility programs
- Desktop apps: Cross-platform UI libraries are available, including from GTK bindings to fully-featured D widget kits like Dlang UI
- Web apps and services: Full-stack or backend web frameworks are available. Vibe.d is the most popular full-stack web framework for D. The LDC compiler can generate WASM from D source files
- Mobile app libraries: Similar to the Gomobile project, you can use D to create Android and iOS shared libraries
D offers features beyond systems programming, so you can use it for building literally any program.
Conclusion
The unpopularity of D is surprising. If we compare D with C++, D wins in most scenarios. D also has enough modern features to compete with Rust, Zig, and Go. If you start building a serious project with D, the only frustration will be the lack of open-source libraries, tooling, and a small developer community; the D language will never disappoint you.
From all C++ alternative languages I tried, D offers the best C/C++ interop. Another highlight is that D lets you choose your code abstraction level — you can even write lightweight programs without embedding the D runtime, or write portable D cross-platform programs by embedding the D runtime — default compiled D programs depend only on the C runtime.
Let's hope D gets the popularity it deserves someday.
Thanks for reading.