Contents
Grammar
D is statically typed. Every expression has a type. Types constrain the values an expression can hold, and determine the semantics of operations on those values.
Type: TypeCtorsopt BasicType TypeSuffixesopt TypeCtors: TypeCtor TypeCtor TypeCtors TypeCtor: const immutable inout shared BasicType: FundamentalType . QualifiedIdentifier QualifiedIdentifier Typeof Typeof . QualifiedIdentifier TypeCtor ( Type ) Vector TraitsExpression MixinType Vector: __vector ( VectorBaseType ) VectorBaseType: Type FundamentalType: void ArithmeticType ArithmeticType: bool byte ubyte short ushort int uint long ulong cent ucent char wchar dchar float double real ifloat idouble ireal cfloat cdouble creal TypeSuffixes: TypeSuffix TypeSuffixesopt TypeSuffix: * [ ] [ AssignExpression ] [ AssignExpression .. AssignExpression ] [ Type ] delegate Parameters MemberFunctionAttributesopt function Parameters FunctionAttributesopt QualifiedIdentifier: Identifier Identifier . QualifiedIdentifier TemplateInstance TemplateInstance . QualifiedIdentifier Identifier [ AssignExpression ] Identifier [ AssignExpression ] . QualifiedIdentifier
- Basic Data Types are leaf types.
- Derived Data Types build on leaf types.
- User-Defined Types are aggregates of basic and derived types.
Basic Data Types
| Keyword | Default Initializer (.init) | Description |
|---|---|---|
| void | no default initializer | void has no value |
| bool | false | boolean value |
| byte | 0 | signed 8 bits |
| ubyte | 0u | unsigned 8 bits |
| short | 0 | signed 16 bits |
| ushort | 0u | unsigned 16 bits |
| int | 0 | signed 32 bits |
| uint | 0u | unsigned 32 bits |
| long | 0L | signed 64 bits |
| ulong | 0uL | unsigned 64 bits |
| 0 | signed 128 bits | |
| 0u | unsigned 128 bits | |
| float | float.nan | 32 bit floating point |
| double | double.nan | 64 bit floating point |
| real | real.nan | largest floating point size available |
| ifloat.nan | imaginary float | |
| idouble.nan | imaginary double | |
| ireal.nan | imaginary real | |
| cfloat.nan | a complex number of two float values | |
| cdouble.nan | complex double | |
| creal.nan | complex real | |
| char | '\xFF' | unsigned 8 bit (UTF-8 code unit) |
| wchar | '\uFFFF' | unsigned 16 bit (UTF-16 code unit) |
| dchar | '\U0000FFFF' | unsigned 32 bit (UTF-32 code unit) |
Endianness of basic types is part of the ABI
Implementation Defined: The real floating point type has at least the range and precision of the double type. On x86 CPUs it is often implemented as the 80 bit Extended Real type supported by the x86 FPU.
Note: Complex and imaginary types ifloat, idouble, ireal, cfloat, cdouble, and creal have been deprecated in favor of std.complex.Complex.
Derived Data Types
- Pointers
- Static Arrays
- Dynamic Arrays
- Associative Arrays
- Function Types
- Delegate Types
- Type Sequences
int* p; int[2] sa; int[] da; int[string] aa; void function() fp; import std.meta : AliasSeq; AliasSeq!(int, string) tsi;
Pointers
A pointer value is a memory address. A pointer to type T has a value which is a reference to an instance of type T. It is commonly called a pointer to T and its type is T*. To access the pointed-to value, use the * dereference operator:
int* p; assert(p == null); p = new int(5); assert(p != null); assert(*p == 5); (*p)++; assert(*p == 6);
If a pointer has a null value, it is not pointing to valid data.
When a pointer to T is dereferenced, it must either have a null value, or point to a valid instance of type T.
Implementation Defined:
- The behavior when a null pointer is dereferenced. Typically the program will be aborted - that is required in @safe code.
Undefined Behavior: dereferencing a pointer that is not null and does not point to a valid instance of type T.
To set a pointer to point at an existing lvalue, use the & address of operator:
int i = 2; int* p = &i; assert(p == &i); assert(*p == 2); *p = 4; assert(i == 4);
See also:
User-Defined Types
Type Conversions
See also: CastExpression.
Pointer Conversions
Any pointer implicitly converts to a void pointer - see below.
Casting between pointers and non-pointers is allowed. Some pointer casts are disallowed in @safe code.
Best Practices: do not cast any pointer to a non-pointer type that points to data allocated by the garbage collector.
Implicit Conversions
Implicit conversions are used to automatically convert types as required. The rules for integers are detailed in the next sections.
- An enum can be implicitly converted to its base type (but going the other way requires an explicit conversion).
- noreturn implicitly converts to any type.
- Static and dynamic arrays implicitly convert to void arrays. The void element type for the array may need a type qualifier, depending on the source element type:
- An array with non-mutable elements will implicitly convert to const(void)[], but not void[].
- An array with shared elements will implicitly convert to shared(void)[], but not void[].
- Any pointer implicitly converts to a void pointer. As for void arrays, the void element type may need a type qualifier.
- Function pointers and delegates can convert to covariant types.
void main() { noreturn n; int i = n; void* p = &i; const int[] a; const(void)[] cv = a; } void f(int x) pure; void function(int) fp = &f;
See also Implicit Qualifier Conversions.
Class Conversions
A derived class can be implicitly converted to its base class, but going the other way requires an explicit cast. For example:
class Base {} class Derived : Base {} Base bd = new Derived(); Derived db = cast(Derived)new Base();
A dynamic array, say x, of a derived class can be implicitly converted to a dynamic array, say y, of a base class iff elements of x and y are qualified as being either both const or both immutable.
class Base {} class Derived : Base {} const(Base)[] ca = (const(Derived)[]).init; immutable(Base)[] ia = (immutable(Derived)[]).init;
A static array, say x, of a derived class can be implicitly converted to a static array, say y, of a base class iff elements of x and y are qualified as being either both const or both immutable or both mutable (neither const nor immutable).
class Base {} class Derived : Base {} Base[3] ma = (Derived[3]).init; const(Base)[3] ca = (const(Derived)[3]).init; immutable(Base)[3] ia = (immutable(Derived)[3]).init;
Integer Promotions
Integer Promotions are conversions of the following types:
| from | to |
|---|---|
| bool | int |
| byte | int |
| ubyte | int |
| short | int |
| ushort | int |
| char | int |
| wchar | int |
| dchar | uint |
If an enum has as a base type one of the types in the left column, it is converted to the type in the right column.
Integer promotion applies to each operand of a binary expression:
void fun() { byte a; auto b = a + a; static assert(is(typeof(b) == int)); ushort d; int e = d * d; static assert(is(typeof(int() * d) == int)); dchar f; static assert(is(typeof(f - f) == uint)); }
Rationale:
- 32-bit integer operations are often faster than smaller integer types for single variables on modern architectures.
- Promotion helps avoid accidental overflow which is more common with small integer types.
Usual Arithmetic Conversions
The usual arithmetic conversions convert operands of binary operators to a common type. The operands must already be of arithmetic types. The following rules are applied in order, looking at the base type:
- If either operand is real, the other operand is converted to real.
- Else if either operand is double, the other operand is converted to double.
- Else if either operand is float, the other operand is converted to float.
- Else the integer promotions above are done on each operand,
followed by:
- If both are the same type, no more conversions are done.
- If both are signed or both are unsigned, the smaller type is converted to the larger.
- If the signed type is larger than the unsigned type, the unsigned type is converted to the signed type.
- The signed type is converted to the unsigned type.
Rationale: The above rules follow C99, which makes porting code from C easier.
Example: Signed and unsigned conversions:
int i; uint u; static assert(is(typeof(i + u) == uint)); static assert(is(typeof(short() + u) == uint)); static assert(is(typeof(ulong() + i) == ulong)); static assert(is(typeof(long() - u) == long)); static assert(is(typeof(long() * ulong()) == ulong));
Example: Floating point:
float f; static assert(is(typeof(f + ulong()) == float)); double d; static assert(is(typeof(f * d) == double)); static assert(is(typeof(real() / d) == real));
Enum Operations
If one or both of the operand types is an enum after undergoing the above conversions, the result type is determined as follows:
- If the operands are the same type, the result will be of that type.
- If one operand is an enum and the other is the base type of that enum, the result is the base type.
- If the two operands are different enums, the result is the closest base type common to both. A base type being closer means there is a shorter sequence of conversions to base type to get there from the original type.
enum E { a, b, c } enum F { x, y } void test() { E e = E.a; e = e | E.c; int i = e + 4; e += 4; F f; i = e | f; }
Note: Above, e += 4 compiles because the operator assignment is equivalent to e = cast(E)(e + 4).
Integer Type Conversions
An integer of type I implicitly converts to another integer type J when J.sizeof >= I.sizeof.
void f(byte b, ubyte ub, short s) { b = ub; ub = b; s = b; b = s; }
Integer values cannot be implicitly converted to another type that cannot represent the integer bit pattern after integral promotion. For example:
ubyte u1 = -1; ushort u2 = -1; uint u3 = -1; ulong u4 = -1;
Floating Point Type Conversions
- Integral types implicitly convert to floating point types.
- Floating point types cannot be implicitly converted to integral types.
void f(int i, float f) { f = i; i = f; }
- Complex or imaginary floating point types cannot be implicitly converted to non-complex floating point types.
- Non-complex floating point types cannot be implicitly converted to imaginary floating point types.
Value Range Propagation
Besides type-based implicit conversions, D allows certain integer expressions to implicitly convert to a narrower type after integer promotion. This works by analysing the minimum and maximum possible range of values for each expression. If that range of values matches or is a subset of a narrower target type's value range, implicit conversion is allowed. If a subexpression is known at compile-time, that can further narrow the range of values.
void fun(char c, int i, ubyte b) { short s = c + 100; ubyte j = i & 0x3F; ushort k = i & 0x14A; k = i & b; s = b + b; }
Note the implementation does not track the range of possible values for mutable variables:
void fun(int i) { ushort s = i & 0xff; ubyte b = s & 0xff; const int c = i & 0xff; b = c; }
- For more information, see the dmc article.
- See also: https://en.wikipedia.org/wiki/Value_range_analysis.
void
A void value cannot be accessed directly. The void type is notably used for:
- The return type of a function that doesn't have a result.
- The base type for an untyped pointer - see Pointer Conversions.
- Void arrays.
- Ignoring a value by casting to void.
void.sizeof is 1 (not 0).
Rationale: The size must be 1 to make void* arithmetic work like it does in C. It also makes the length of a void array equivalent to the number of bytes in the array.
bool
The bool type is a byte-size type that can only hold the value true or false.
The only operators that can accept operands of type bool are: & |, ^, &=, |=, ^=, !, &&, ||, and ?:.
A bool value can be implicitly converted to any integral type, with false becoming 0 and true becoming 1.
The numeric literals 0 and 1 can be implicitly converted to the bool values false and true, respectively. Casting an expression to bool means testing !=0 for an ArithmeticType, and !=null for pointers or reference types. See Boolean Conversion for details.
Undefined Behavior:
- Interpreting a value with a byte representation other than 0 or 1 as bool (e.g. an overlapped union field).
- Reading a void-initialized bool.
byte i = 2; bool b = cast(bool) i; assert(b); bool* p = cast(bool*) &i;
Function Types
A function type has the form:
StorageClassesopt Type Parameters FunctionAttributesopt
Function types are not included in the Type grammar. A function type e.g. int(int) can be aliased. A function type is only used for type tests or as the target type of a pointer.
Instantiating a function type is illegal. Instead, a pointer to function or delegate can be used. Those have these type forms respectively:
Type function Parameters FunctionAttributesopt Type delegate Parameters MemberFunctionAttributesopt
void f(int); alias Fun = void(int); static assert(is(typeof(f) == Fun)); static assert(is(Fun* == void function(int)));
See Function Pointers.
Delegates
Delegates are an aggregate of two pieces of data, a context pointer and a function pointer. A valid delegate holds either:
- An object reference and a pointer to a non-static member function. The object reference forms the this pointer when the function is called.
- A pointer to a closure and a pointer to a nested function.
The .ptr property of a delegate will return the context pointer value as a void*.
The .funcptr property of a delegate will return the function pointer value as a function type.
Delegates are declared and initialized similarly to function pointers:
void func(int) {} void function(int) fp; void delegate(int) dg; class OB { void member(int) {} } void main() { OB o = new OB; fp = &func; dg = &o.member; assert(dg.ptr == cast(void*) o); assert(dg.funcptr == &OB.member); dg = (int i) { o.member(i); }; }
Delegates cannot be initialized with static member functions or non-member functions.
Delegates are called analogously to function pointers:
fp(3); dg(3);
See:
The equivalent of member function pointers can be constructed using anonymous lambda functions:
class C { int a; int foo(int i) { return i + a; } } auto mfp = function(C self, int i) { return self.foo(i); }; auto c = new C(); mfp(c, 1);
typeof
Typeof: typeof ( Expression ) typeof ( return )
The first form gives the type of an expression. It can be used anywhere a BasicType is expected, such as in a declaration. It is also useful as a PrimaryExpression in a sub-expression. For example:
void func(int i) { typeof(i) j; typeof(3 + 6.0) x; typeof(1)* p; static assert(is(typeof(p) == int*)); typeof(p)[int] aa; static assert(is(typeof(aa) == int*[int])); auto d = cast(typeof(1.0)) i; static assert(is(typeof(d) == double)); static assert(typeof('c').sizeof == 1); Exception[2] sa; Exception ex = new typeof(sa[0])("message"); }
Expression is not evaluated, it is used purely to generate the type:
void main() { int i = 1; typeof(++i) j; assert(i == 1); }
If Expression is a ValueSeq, typeof will produce a TypeSeq containing the types of each element.
typeof(null) is useful to get the type of the null literal.
Best Practices: Typeof is most useful in writing generic template code.
Special Cases
- typeof(return) will, when inside a function scope, give the return type of that function.
- typeof(this) will generate the type of what this would be in a non-static member function, even if not in a member function.
- Analogously, typeof(super) will generate the type of what super would be in a non-static member function.
class A { } class B : A { typeof(this) x; typeof(super) y; } struct C { static typeof(this) z; typeof(super) q; } typeof(this) r;
If the expression is a Property Function, typeof gives its return type.
struct S { @property int foo() { return 1; } } typeof(S.foo) n;
If the expression is a Template, typeof gives the type void.
template t {} static assert(is(typeof(t) == void));
Mixin Types
MixinType: mixin ( ArgumentList )
Each AssignExpression in the ArgumentList is evaluated at compile time, and the result must be representable as a string. The resulting strings are concatenated to form a string. The text contents of the string must be compilable as a valid Type, and is compiled as such.
void test(mixin("int")* p) { mixin("int")[] a; mixin("int[]") b; }
Aliased Types
size_t
size_t is an alias to one of the unsigned integral basic types, and represents a type that is large enough to represent an offset into all addressable memory.
ptrdiff_t
ptrdiff_t is an alias to the signed integral basic type the same size as size_t.
string
A string is a special case of an array.
noreturn
noreturn is the bottom type which can implicitly convert to any type, including void. A value of type noreturn will never be produced and the compiler can optimize such code accordingly.
noreturn.init lowers to assert(0).
A function that never returns has the return type noreturn. This can occur due to e.g. an infinite loop or always throwing an exception. A function returning type noreturn is covariant with a function returning any other type.
noreturn abort(string message); int function(string) fp = &abort; int example(int i) { if (i < 0) { int val = abort("less than zero"); } return i != 0 ? 1024 / i : abort("calculation went awry."); }
noreturn is defined as typeof(*null). This is because dereferencing a null literal (typically) halts execution.
See also: ThrowExpression
Copyright © 1999-2026 by the D Language Foundation | Page generated by Ddoc on Thu Jan 15 22:47:58 2026