const qualifier is familiar to everyone, it is present in many modern C-like languages and used in many different contexts. In the most general sense, it means an immutable variable that can only be initialized once. In dynamically typed languages const usually means exatly that, but in statically typed world, including D as well, there are different types of immutability, and understanding them is cruisal to write reliable software.

Why do we need it?

By executing a computer program we utilize a processor to perform computations. If your computation is something more complex than a simple mathematic formula, then your program will have a state — that is, an intermediate data in memory that is kept for later use. All the comlexity of a program usually comes from state management and memory safety, because raw memory is just a set of addressable bytes without any intrinsic structure. To reduce this complexity and prevent invalid behaviour, one can define a data model and constrain it with rules — example of such set of rules is a type system, which defines data types and their properties. Given a static type system in a language, a compiler can do semantic analysis and reject a program that tries to do something illegal.

const in D

In C-like languages const is an additional rule in a type system that basically says “any variable of this type can’t be changed”. D also has const qualifier, and it is very important to understand its differencies from const in C++. In C++ it is not transitive, meaning that const objects can have mutable members. In D const is transitive. Once it is applied to a type, it applies to every member of that type:

class Foo
{
int x = 10;
}
const(Foo) foo = new Foo();
foo.x = 5; // illegal!

const vs immutable

D also has immutable qualifier, and this often cause confusion. Immutable type means that object of that type, once created, cannot be changed. const type means that object cannot be changed by an identifier of that type. It may, however, be changed by another, mutable identifier. So const can be seen as an interface that ensures read-only access.

class Foo
{
int x = 10;
}
int bar(Foo foo1, Foo foo2)
{
return foo1.x + foo2.x;
}
void main()
{
immutable(Foo) foo1 = new Foo();
Foo foo2 = new Foo();
bar(foo1, foo2);
}
int bar(const(Foo) foo1, const(Foo) foo2)
{
return foo1.x + foo2.x;
}
const int x = 10;
int* px = cast(int*)&x;
*px = 5;

const vs inout

Another type qualifier in D which is somewhat obscure for most beginners is inout. Imagine your function has to return a type with the same mutability that was passed as a parameter:

auto halfArray(const(int)[] a)
{
return a[$/2..$];
}
immutable(int)[] arr1 = [4, 5, 7, 8];
immutable(int)[] arr2 = halfArray(arr1);
auto halfArray(inout(int)[] a)
{
return a[$/2..$];
}
const(int)[] arr1 = [4, 5, 7, 8];
const(int)[] arr2 = halfArray(arr1);
int[] arr1 = [4, 5, 7, 8];
int[] arr2 = halfArray(arr1);

const methods

constapplied to a method’s return type is a special case:

class Foo
{
int prop() const
{
return _prop;
}
protected int _prop = 0;
}
class Foo
{
int prop() const
{
_prop = 8;
return _prop;
}
protected int _prop = 0;
}
import std.string;class Foo
{
const(char*) prop() const
{
return _prop.toStringz();
}
protected string _prop = "something";
}

ref const

It is possible to have ref const identifiers. They are useful, for example, in foreach statement to ensure that elements of an aggregate are not changed by reference:

void foo(ref const(int) v)
{
v = 0; // illegal
}
int[] arr = [1, 2, 3, 4, 5];foreach(ref const v; arr)
{
foo(v);
v = 0; // illegal
}

3D game engine developer