Declaration can be used inside a function body for aDeclarationStatement,as well as outside a function as it is included inDeclDef.
Declaration:FuncDeclarationVarDeclarationsAliasDeclarationAliasAssignAggregateDeclarationEnumDeclarationImportDeclarationConditionalDeclarationStaticForeachDeclarationStaticAssertTemplateDeclarationTemplateMixinDeclarationTemplateMixin
AggregateDeclaration:ClassDeclarationInterfaceDeclarationStructDeclarationUnionDeclaration
VarDeclarations:StorageClassesoptBasicTypeTypeSuffixesoptIdentifierInitializers;AutoDeclarationIdentifierInitializers:IdentifierInitializerIdentifierInitializer,IdentifierInitializersIdentifierInitializer:IdentifierIdentifierTemplateParametersopt=InitializerDeclarator:TypeSuffixesoptIdentifier
See also:
SeeType Classes vs. Storage Classes.
StorageClasses:StorageClassStorageClassStorageClassesStorageClass:LinkageAttributeAlignAttributeAtAttributedeprecatedenumstaticexternabstractfinaloverridesynchronizedautoscopeconstimmutableinoutshared__gsharedPropertynothrowpureref
Declaration syntax generally reads right to left, including arrays:
int x;// x is an intint* x;// x is a pointer to intint** x;// x is a pointer to a pointer to intint[] x;// x is an array of intsint*[] x;// x is an array of pointers to intsint[]* x;// x is a pointer to an array of intsint[3] x;// x is a static array of 3 intsint[3][5] x;// x is a static array of 5 static arrays of 3 intsint[3]*[5] x;// x is a static array of 5 pointers to static arrays of 3 ints
SeePointers,ArraysandTypeSuffix.
Function Pointersare declared using thefunction keyword:
intfunction(char) x;// x is a pointer to// a function taking a char argument// and returning an intintfunction(char)[] x;// x is an array of// pointers to functions// taking a char argument// and returning an int
C-style array, function pointer and pointer to array declarations arenot supported. The following C declarations are for comparison only:
int x[3]; // C static array of 3 intsint x[3][5]; // C static array of 3 arrays of 5 intsint (*x[5])[3]; // C static array of 5 pointers to static arrays of 3 intsint (*x)(char); // C pointer to a function taking a char argument // and returning an intint (*[] x)(char); // C array of pointers to functions // taking a char argument and returning an int
In a declaration declaring multiple symbols, all the declarationsmust be of the same type:
int x, y;// x and y are intsint* x, y;// x and y are pointers to intsint[] 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 intint x[], y; // x is an array/pointer, y is an int
When noInitializer is given, a variable is set to thedefault.init value for its type.
Initializer:VoidInitializerNonVoidInitializerNonVoidInitializer:ArrayInitializerStructInitializerAssignExpression
A variable can be initialized with aNonVoidInitializer. AnArrayInitializer orStructInitializer takes precedence over an expression initializer.
struct S {int i; }S s = {};// struct initializer, not a function literal expressionS[] a = [{2}];// array initializer holding a struct initializer//a = [{2}]; // invalid array literal expression
VoidInitializer:void
Normally a variable will be initialized. However, if a variable initializer isvoid, the variable isnot initialized. Void initializers for variables with a type that may containunsafe values (such as types with pointers) are not allowed in@safe code.
void bad(){int x =void; writeln(x);// print implementation defined value}
void muchWorse(){char[] p =void; writeln(p);// may result in apocalypse}
AutoDeclaration:StorageClassesAutoAssignments;AutoAssignments:AutoAssignmentAutoAssignments,AutoAssignmentAutoAssignment:IdentifierTemplateParametersopt=Initializer
If a declaration starts with aStorageClass and has aNonVoidInitializer from which the type can be inferred, the type on the declaration can be omitted.
static x = 3;// x is type intauto y = 4u;// y is type uintauto s ="Apollo";// s is type immutable(char)[] i.e., stringclass C { ... }auto c =new C();// c is a handle to an instance of class C
TheNonVoidInitializer 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.
AnArrayLiteral is inferred to be a dynamic array type rather than a static array:
auto v = ["resistance","is","useless"];// type is string[], not string[3]
TheInitializer for a global or static variable must be evaluatable at compile time. Runtime initialization is done withstatic constructors.
AliasDeclaration:aliasStorageClassesoptBasicTypeTypeSuffixesoptIdentifiers;aliasStorageClassesoptBasicTypeFuncDeclarator;aliasAliasAssignments;Identifiers:IdentifierIdentifier,IdentifiersAliasAssignments:AliasAssignmentAliasAssignments,AliasAssignmentAliasAssignment:IdentifierTemplateParametersopt=StorageClassesoptTypeIdentifierTemplateParametersopt=FunctionLiteralIdentifierTemplateParametersopt=StorageClassesoptTypeParametersMemberFunctionAttributesopt
AnAliasDeclaration 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:
alias 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?
A symbol can be declared as analias 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 intt2 v2;// v2 is type intt3 v3;// v3 is type intt4 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:
staticimport string;...alias strlen = string.strlen;
Aliases 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 overloadsint foo(int a) {return 3; }// redeclare hidden overloadalias 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}
Variables can be aliased, expressions cannot:
int i = 0;alias a = i;// OKalias b = a;// alias a variable aliasa++;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{staticint i = 0;int j;alias a = j;// OKvoid inc() { a++; }}alias a = S.i;// OKa++;assert(S.i == 1);alias b = S.j;// allowedstaticassert(b.offsetof == 0);//b++; // error, no instance of S//S.a++; // error, no instance of SS s = S(5);s.inc();assert(s.j == 6);//alias c = s.j; // scheduled for deprecation
Function types can be aliased:
alias Fun =int(string);int fun(string) {return 0;}staticassert(is(typeof(fun) == Fun));alias MemberFun1 =int()const;alias MemberFun2 =constint();// leading attributes apply to the func, not the return typestaticassert(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: 6alias Foo =voidfunction(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 =voidfunction(int,int,int=9);alias Baz =voidfunction(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);}
AliasAssign:Identifier=Type
AnAliasDeclaration can have a new value assigned to it with anAliasAssign:
template Gorgon(T){alias A =long; A = T;// assign new value to Aalias Gorgon = A;}pragma(msg, Gorgon!int);// prints int
import std.meta : AliasSeq;staticif (0)// recursive method for comparison{template Reverse(T...) {staticif (T.length == 0)alias Reverse = AliasSeq!();elsealias Reverse = AliasSeq!(Reverse!(T[1 .. T.length]), T[0]); }}else// iterative method minimizes template instantiations{template Reverse(T...) {alias A = AliasSeq!();staticforeach (t; T) A = AliasSeq!(t, A);// Alias Assignalias Reverse = A; }}enum X = 3;alias TK = Reverse!(int,constuint, X);pragma(msg, TK);// prints tuple(3, (const(uint)), (int))
AliasReassignment:Identifier=StorageClassesoptTypeIdentifier=FunctionLiteralIdentifier=StorageClassesoptBasicTypeParametersMemberFunctionAttributesopt
An alias declaration inside a template can be reassigned a new value.
import std.meta : AliasSeq;template staticMap(alias F, Args...){alias A = AliasSeq!();staticforeach (Arg; Args) A = AliasSeq!(A, F!Arg);// alias reassignmentalias staticMap = A;}enum size(T) = T.sizeof;staticassert(staticMap!(size,char,wchar,dchar) == AliasSeq!(1, 2, 4));
TheIdentifier must resolve to a lexically precedingAliasDeclaration. Both must be members of the sameTemplateDeclaration.
The right hand side of theAliasReassignment replaces the right hand side of theAliasDeclaration.
Once theAliasDeclaration has been referred to in any context other than the right hand side of anAliasReassignment it can no longer be reassigned.
Variable declarations with the storage classextern are not allocatedstorage within the module. They must be defined in some other object file with amatching name which is then linked in.
Anextern declaration can optionally be followed by anexternlinkage attribute. If there is no linkageattribute it defaults toextern(D):
// variable allocated and initialized in this module with C linkageextern(C)int foo;// variable allocated outside this module with C linkage// (e.g. in a statically linked C library or another module)externextern(C)int bar;
Type qualifers andstorage classes are distinct concepts.
Atype 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, theimmutable 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)
Astorage 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 theconst storage class to indicate that it does not modify its implicitthis argument:
struct S{int x;int method()const {//x++; // Error: this method is const and cannot modify this.xreturn x;// OK: we can still read this.x }}
Although some keywords can beused as both a type qualifier and a storage class, there are some storage classes that cannot be used to construct new types, such asref.
A parameterdeclared withref is passed by reference:
void func(refint 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 bedeclared asref, meaning their return value is passed by reference:
refint func2(){staticint 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();staticassert(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().}
From version 2.111,ref can be used to declare local, static, extern, and global variables.
struct S {int a; }void main(){ S s;refint r = s.a; r = 3;assert(s.a == 3);}
auto ref can also be used to declare local, static, extern, and global variables.
void f(){autoref x = 0;autoref y = x;staticassert(!__traits(isRef, x));staticassert(__traits(isRef, y));}
See also:auto ref template function parameters.
Some keywords, such asconst, 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? */constint* func()// a const function {//++p; // error, this.p is const//return p; // error, cannot convert const(int)* to int*returnnull; }const(int)* func()// a function returning a pointer to a const int { ++p;// ok, this.p is mutablereturn p;// ok, int* can be implicitly converted to const(int)* }int* p;}
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; }}