GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing dlang.org — Declarations - D Programming Language Language Reference
Report a bug
If you spot a problem with this page, click here to create a GitHub issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page.
Requires a signed-in GitHub account. This works well for small changes.
If you'd like to make larger changes you may want to consider using
a local clone.
Declarations Contents
GrammarDeclaration can be used inside a function body for a DeclarationStatement, as well as outside a function as it is included in DeclDef. Declaration:
FuncDeclaration
VarDeclarations
AliasDeclaration
AggregateDeclaration
EnumDeclaration
ImportDeclaration
ConditionalDeclaration
StaticForeachDeclaration
StaticAssert
TemplateDeclaration
TemplateMixinDeclaration
TemplateMixin
AggregatesAggregateDeclaration:
ClassDeclaration
InterfaceDeclaration
StructDeclaration
UnionDeclaration
Variable DeclarationsVarDeclarations:
StorageClassesopt BasicType TypeSuffixesopt IdentifierInitializers ;
AutoDeclaration
TuplePattern = AssignExpression ;
IdentifierInitializers:
IdentifierInitializer
IdentifierInitializer , IdentifierInitializers
IdentifierInitializer:
Identifier
Identifier TemplateParametersopt = Initializer
BitfieldDeclarator
BitfieldDeclarator = Initializer
BitfieldDeclarator:
: AssignExpression
Identifier : ConditionalExpression
Declarator:
TypeSuffixesopt Identifier
See also: Declaration Syntax. Storage ClassesSee Type Classes vs. Storage Classes. StorageClasses:
StorageClass
StorageClass StorageClasses
StorageClass:
LinkageAttribute
AlignAttribute
AtAttribute
deprecated
enum
static
extern
abstract
final
override
synchronized
auto
scope
const
immutable
inout
shared
__gshared
Property
nothrow
pure
ref
Declaration SyntaxDeclaration syntax generally reads right to left, including arrays: int x; // x is an int int* x; // x is a pointer to int int** x; // x is a pointer to a pointer to int int[] x; // x is an array of ints int*[] x; // x is an array of pointers to ints int[]* x; // x is a pointer to an array of ints int[3] x; // x is a static array of 3 ints int[3][5] x; // x is a static array of 5 static arrays of 3 ints int[3]*[5] x; // x is a static array of 5 pointers to static arrays of 3 ints See Pointers, Arrays and TypeSuffix. Function PointersFunction Pointers are declared using the function keyword: int function(char) x; // x is a pointer to // a function taking a char argument // and returning an int int function(char)[] x; // x is an array of // pointers to functions // taking a char argument // and returning an int C-Style DeclarationsC-style array, function pointer and pointer to array declarations are not supported. The following C declarations are for comparison only: int x[3]; // C static array of 3 ints
int x[3][5]; // C static array of 3 arrays of 5 ints
int (*x[5])[3]; // C static array of 5 pointers to static arrays of 3 ints
int (*x)(char); // C pointer to a function taking a char argument
// and returning an int
int (*[] x)(char); // C array of pointers to functions
// taking a char argument and returning an int
Rationale:
Declaring Multiple SymbolsIn a declaration declaring multiple symbols, all the declarations must be of the same type: int x, y; // x and y are ints int* x, y; // x and y are pointers to ints int[] x, y; // x and y are arrays of ints This is in contrast to C: int x, *y; // x is an int, y is a pointer to int int x[], y; // x is an array/pointer, y is an int InitializationWhen no Initializer is given, a variable is set to the default .init value for its type. InitializersInitializer:
VoidInitializer
NonVoidInitializer
NonVoidInitializer:
ArrayInitializer
StructInitializer
AssignExpression
A variable can be initialized with a NonVoidInitializer. An ArrayInitializer or StructInitializer takes precedence over an expression initializer. struct S { int i; } S s = {}; // struct initializer, not a function literal expression S[] a = [{2}]; // array initializer holding a struct initializer //a = [{2}]; // invalid array literal expression Void InitializationVoidInitializer:
void
Normally a variable will be initialized. However, if a variable initializer is void, the variable is not initialized. Void initializers for variables with a type that may contain unsafe values (such as types with pointers) are not allowed in @safe code. Implementation Defined: If a void initialized variable's value is
used before it is set, its value is implementation defined.
void bad() { int x = void; writeln(x); // print implementation defined value } Undefined Behavior: If a void initialized variable's value is
used before it is set, and the value is a reference, pointer or an instance
of a struct with an invariant, the behavior is undefined.
void muchWorse() { char[] p = void; writeln(p); // may result in apocalypse } Best Practices:
Implicit Type InferenceAutoDeclaration:
StorageClasses AutoAssignments ;
AutoAssignments:
AutoAssignment
AutoAssignments , AutoAssignment
AutoAssignment:
Identifier TemplateParametersopt = Initializer
TuplePattern = AssignExpression
If a declaration starts with a StorageClass and has a NonVoidInitializer from which the type can be inferred, the type on the declaration can be omitted. static x = 3; // x is type int auto y = 4u; // y is type uint auto s = "Apollo"; // s is type immutable(char)[] i.e., string class C { ... } auto c = new C(); // c is a handle to an instance of class C The NonVoidInitializer cannot contain forward references (this restriction may be removed in the future). The implicitly inferred type is statically bound to the declaration at compile time, not run time. An ArrayLiteral is inferred to be a dynamic array type rather than a static array: auto v = ["resistance", "is", "useless"]; // type is string[], not string[3] Unpacking DeclarationsVarDeclarations and AutoDeclaration support initializing one or more variables in a tuple pattern from an AssignExpression. The AssignExpression must implicitly convert to a value sequence. TuplePattern:
( TuplePatternComponents )
TuplePatternComponents:
TuplePatternComponent ,
TuplePatternComponent , TuplePatternComponent
TuplePatternComponent , TuplePatternComponents
TuplePatternComponent:
StorageClassesopt Identifier
StorageClassesopt BasicType TypeSuffixesopt Identifier
StorageClassesopt TuplePattern
import std.typecons : tuple; (int a, auto b) = tuple(1, "2"); // unpacking declaration assert(a == 1); assert(b == "2"); The unpacking declaration above is lowered to e.g.: auto temp = tuple(1, "2"); int a = temp[0]; auto b = temp[1]; See also: std.typecons.Tuple. When the tuple pattern has no storage class, each component that declares a variable must either have a type or a storage class applying to it: (int a, b) = tuple(1, "2"); // Error: `b` has no type or storage class When there is a storage class for a pattern, each component of the pattern can be just an identifier. Each variable's type will be inferred from the corresponding sequence element: import std.typecons : tuple; auto (a, b) = tuple(1, "2"); static assert(is(typeof(a) == int)); static assert(is(typeof(b) == string)); auto (a, immutable b, c) = tuple(1, "2", 3.0); static assert(is(typeof(a) == int)); static assert(is(typeof(b) == immutable string)); static assert(is(typeof(c) == double)); Patterns can be nested: auto t = tuple(1, tuple("2", 3.0)); // the following are equivalent: auto (a, (b, c)) = t; (int a, (string b, double c)) = t; (int a, auto (b, c)) = t; (int a, (auto b, auto c)) = t; static or enum can be applied to the whole declaration, but not to individual components. Global and Static InitializersThe Initializer for a global or static variable must be evaluatable at compile time. Runtime initialization is done with static constructors. Implementation Defined: Whether some pointers can be initialized with the addresses of other
functions or data.
Statically Allocated DataVarDeclarations outside a function or aggregate type will be statically allocated. By default, such a variable will be thread-local unless it has one of the following StorageClasses: By default, VarDeclarations inside a function will be allocated on the stack. The following StorageClasses will instead statically allocate data, even inside a function or aggregate type:
Note: When StorageClasses includes enum
or extern, a variable declaration
will not have any storage allocated.
Implementation Defined: Static declarations whose initializer is void or whose
bits are all zero may be listed in the
.bss section of the binary to save
storage space.
Alias Declarations2024 Edition: Use the AliasAssignments form only -
see legacy.
AliasDeclaration: alias StorageClassesopt BasicType TypeSuffixesopt Identifiers ; alias StorageClassesopt BasicType FuncDeclarator ; alias AliasAssignments ; Identifiers: Identifier Identifier , Identifiers AliasAssignments: AliasAssignment AliasAssignments , AliasAssignment AliasAssignment: Identifier TemplateParametersopt = StorageClassesopt Type Identifier TemplateParametersopt = FunctionLiteral Identifier TemplateParametersopt = StorageClassesopt Type Parameters MemberFunctionAttributesopt An AliasDeclaration creates a symbol name that refers to a type or another symbol. That name can then be used anywhere that the target may appear. The following can be aliased:
Type Aliasesalias myint = abc.Foo.bar;
Aliased types are semantically identical to the types they are aliased to. The debugger cannot distinguish between them, and there is no difference as far as function overloading is concerned. For example: alias myint = int; void foo(int x) { ... } void foo(myint m) { ... } // error, multiply defined function foo Type aliases can sometimes look indistinguishable from other symbol aliases: alias abc = foo.bar; // is it a type or a symbol? Best Practices: Other than when aliasing simple basic type names,
type alias names should be Capitalized.
Symbol AliasesA symbol can be declared as an alias of another symbol. For example: import planets; alias myAlbedo = planets.albedo; ... int len = myAlbedo("Saturn"); // actually calls planets.albedo() The following alias declarations are valid: template Foo2(T) { alias t = T; } alias t1 = Foo2!(int); alias t2 = Foo2!(int).t; alias t3 = t1.t; alias t4 = t2; t1.t v1; // v1 is type int t2 v2; // v2 is type int t3 v3; // v3 is type int t4 v4; // v4 is type int Aliased symbols are useful as a shorthand for a long qualified symbol name, or as a way to redirect references from one symbol to another: version (Win32) { alias myfoo = win32.foo; } version (linux) { alias myfoo = linux.bar; } Aliasing can be used to 'import' a symbol from an imported module or package into the current scope: static import string; ... alias strlen = string.strlen; Aliasing an Overload SetAliases can also 'import' a set of overloaded functions, that can be overloaded with functions in the current scope: class B { int foo(int a, uint b) { return 2; } } class C : B { // declaring an overload hides any base class overloads int foo(int a) { return 3; } // redeclare hidden overload alias foo = B.foo; } void main() { import std.stdio; C c = new C(); c.foo(1, 2u).writeln; // calls B.foo c.foo(1).writeln; // calls C.foo } Aliasing VariablesVariables can be aliased, expressions cannot: int i = 0; alias a = i; // OK alias b = a; // alias a variable alias a++; b++; assert(i == 2); //alias c = i * 2; // error //alias d = i + i; // error Members of an aggregate can be aliased, however non-static field aliases cannot be accessed outside their parent type. struct S { static int i = 0; int j; alias a = j; // OK void inc() { a++; } } alias a = S.i; // OK a++; assert(S.i == 1); alias b = S.j; // allowed static assert(b.offsetof == 0); //b++; // error, no instance of S //S.a++; // error, no instance of S S s = S(5); s.inc(); assert(s.j == 6); //alias c = s.j; // scheduled for deprecation Aliasing a Function TypeFunction types can be aliased: alias Fun = int(string); int fun(string) {return 0;} static assert(is(typeof(fun) == Fun)); alias MemberFun1 = int() const; alias MemberFun2 = const int(); // leading attributes apply to the func, not the return type static assert(is(MemberFun1 == MemberFun2)); Type aliases can be used to call a function with different default arguments, change an argument from required to default or vice versa: import std.stdio : writeln; void fun(int v = 6) { writeln("v: ", v); } void main() { fun(); // prints v: 6 alias Foo = void function(int=7); Foo foo = &fun; foo(); // prints v: 7 foo(8); // prints v: 8 } import std.stdio : writefln; void main() { fun(4); // prints a: 4, b: 6, c: 7 Bar bar = &fun; //bar(4); // compilation error, because the `Bar` alias // requires an explicit 2nd argument bar(4, 5); // prints a: 4, b: 5, c: 9 bar(4, 5, 6); // prints a: 4, b: 5, c: 6 Baz baz = &fun; baz(); // prints a: 2, b: 3, c: 4 } alias Bar = void function(int, int, int=9); alias Baz = void function(int=2, int=3, int=4); void fun(int a, int b = 6, int c = 7) { writefln("a: %d, b: %d, c: %d", a, b, c); } Alias AssignAliasAssign:
Identifier = Type
An AliasDeclaration can have a new value assigned to it with an AliasAssign: template Gorgon(T) { alias A = long; A = T; // assign new value to A alias Gorgon = A; } pragma(msg, Gorgon!int); // prints int
Best Practices: AliasAssign is particularly useful when using an iterative
computation rather than a recursive one, as it avoids creating
the large number of intermediate templates that the recursive one
engenders.
import std.meta : AliasSeq; static if (0) // recursive method for comparison { template Reverse(T...) { static if (T.length == 0) alias Reverse = AliasSeq!(); else alias Reverse = AliasSeq!(Reverse!(T[1 .. T.length]), T[0]); } } else // iterative method minimizes template instantiations { template Reverse(T...) { alias A = AliasSeq!(); static foreach (t; T) A = AliasSeq!(t, A); // Alias Assign alias Reverse = A; } } enum X = 3; alias TK = Reverse!(int, const uint, X); pragma(msg, TK); // prints tuple(3, (const(uint)), (int)) Alias ReassignmentAliasReassignment:
Identifier = StorageClassesopt Type
Identifier = FunctionLiteral
Identifier = StorageClassesopt BasicType Parameters MemberFunctionAttributesopt
An alias declaration inside a template can be reassigned a new value. import std.meta : AliasSeq; template staticMap(alias F, Args...) { alias A = AliasSeq!(); static foreach (Arg; Args) A = AliasSeq!(A, F!Arg); // alias reassignment alias staticMap = A; } enum size(T) = T.sizeof; static assert(staticMap!(size, char, wchar, dchar) == AliasSeq!(1, 2, 4)); The Identifier must resolve to a lexically preceding AliasDeclaration. Both must be members of the same TemplateDeclaration. The right hand side of the AliasReassignment replaces the right hand side of the AliasDeclaration. Once the AliasDeclaration has been referred to in any context other than the right hand side of an AliasReassignment it can no longer be reassigned. Rationale: Alias reassignment can result in faster compile times and lowered memory consumption,
and requires significantly simpler code than the alternative recursive method.
Extern DeclarationsVariable declarations with the storage class extern are not allocated storage within the module. They must be defined in some other object file with a matching name which is then linked in. An extern declaration can optionally be followed by an extern linkage attribute. If there is no linkage attribute it defaults to extern(D): // variable allocated and initialized in this module with C linkage extern(C) int foo; // variable allocated outside this module with C linkage // (e.g. in a statically linked C library or another module) extern extern(C) int bar; Best Practices: The primary usefulness of Extern Declarations is to
connect with global variables declarations and functions in C or C++ files.
Type Qualifiers vs. Storage ClassesType qualifers and storage classes are distinct concepts. A type qualifier creates a derived type from an existing base type, and the resulting type may be used to create multiple instances of that type. For example, the immutable type qualifier can be used to create variables of immutable type, such as: immutable(int) x; // typeof(x) == immutable(int) immutable(int)[] y; // typeof(y) == immutable(int)[] // typeof(y[0]) == immutable(int) // Type constructors create new types that can be aliased: alias ImmutableInt = immutable(int); ImmutableInt z; // typeof(z) == immutable(int) A storage class, on the other hand, does not create a new type, but describes only the kind of storage used by the variable or function being declared. For example, a member function can be declared with the const storage class to indicate that it does not modify its implicit this argument: struct S { int x; int method() const { //x++; // Error: this method is const and cannot modify this.x return x; // OK: we can still read this.x } } Although some keywords can be used as both a type qualifier and a storage class, there are some storage classes that cannot be used to construct new types, such as ref. ref Storage ClassA parameter declared with ref is passed by reference: void func(ref int i) { i++; // modifications to i will be visible in the caller } void main() { auto x = 1; func(x); assert(x == 2); // However, ref is not a type qualifier, so the following is illegal: //ref(int) y; // Error: ref is not a type qualifier. } Functions can also be declared as ref, meaning their return value is passed by reference: ref int func2() { static int y = 0; return y; } void main() { func2() = 2; // The return value of func2() can be modified. assert(func2() == 2); // However, the reference returned by func2() does not propagate to // variables, because the 'ref' only applies to the return value itself, // not to any subsequent variable created from it: auto x = func2(); static assert(is(typeof(x) == int)); // N.B.: *not* ref(int); // there is no such type as ref(int). x++; assert(x == 3); assert(func2() == 2); // x is not a reference to what func2() returned; it // does not inherit the ref storage class from func2(). } ref VariablesFrom version 2.111, ref can be used to declare local, static, extern, and global variables. struct S { int a; } void main() { S s; ref int r = s.a; r = 3; assert(s.a == 3); } Best Practices: Use a ref variable instead of a pointer when pointer
arithmetic is not needed.
auto ref can also be used to declare local, static, extern, and global variables. void f() { auto ref x = 0; auto ref y = x; static assert(!__traits(isRef, x)); static assert( __traits(isRef, y)); } See also: auto ref template function parameters. Methods Returning a Qualified TypeSome keywords, such as const, can be used both as a type qualifier and a storage class. The distinction is determined by the syntax where it appears. struct S { /* Is const here a type qualifier or a storage class? * Is the return value const(int), or is this a const function that returns * (mutable) int? */ const int* func() // a const function { //++p; // error, this.p is const //return p; // error, cannot convert const(int)* to int* return null; } const(int)* func() // a function returning a pointer to a const int { ++p; // ok, this.p is mutable return p; // ok, int* can be implicitly converted to const(int)* } int* p; } Best Practices: To avoid confusion, the type qualifier
syntax with parentheses should be used for return types,
and function storage classes should be written on the right-hand side of the
declaration instead of the left-hand side where it may be visually
confused with the return type:
struct S { // Now it is clear that the 'const' here applies to the return type: const(int) func1() { return 1; } // And it is clear that the 'const' here applies to the function: int func2() const { return 1; } } Copyright © 1999-2026 by the D Language Foundation | Page generated by
Ddoc on Mon Nov 9 05:59:18 2026
|
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |