FuncDeclaration:StorageClassesoptBasicTypeFuncDeclaratorFunctionBodyStorageClassesoptBasicTypeFuncDeclaratorMissingFunctionBodyAutoFuncDeclarationAutoFuncDeclaration:StorageClassesIdentifierFuncDeclaratorSuffixFunctionBodyFuncDeclarator:TypeSuffixesoptIdentifierFuncDeclaratorSuffixFuncDeclaratorSuffix:ParametersMemberFunctionAttributesoptTemplateParametersParametersMemberFunctionAttributesoptConstraintopt
Parameters:(ParameterListopt)ParameterList:ParameterParameter,ParameterListoptVariadicArgumentsAttributesopt...Parameter:ParameterDeclarationParameterDeclaration...ParameterDeclaration=AssignExpressionParameterDeclaration=AssignExpression...ParameterDeclaration:ParameterAttributesoptBasicTypeDeclaratorParameterAttributesoptTypeParameterAttributes:ParameterStorageClassUserDefinedAttributeParameterAttributesParameterStorageClassParameterAttributesUserDefinedAttributeParameterStorageClass:autoTypeCtorfinalinlazyoutrefreturnscopeVariadicArgumentsAttributes:VariadicArgumentsAttributeVariadicArgumentsAttributeVariadicArgumentsAttributesVariadicArgumentsAttribute:constimmutablereturnscopeshared
See also:parameter storage classes.
FunctionAttributes:FunctionAttributeFunctionAttributeFunctionAttributesFunctionAttribute:FunctionAttributeKwdPropertyAtAttributeMemberFunctionAttributes:MemberFunctionAttributeMemberFunctionAttributeMemberFunctionAttributesMemberFunctionAttribute:constimmutableinoutreturnscopesharedFunctionAttribute
FunctionBody:SpecifiedFunctionBodyShortenedFunctionBodySpecifiedFunctionBody:dooptBlockStatementFunctionContractsoptInOutContractExpressiondooptBlockStatementFunctionContractsoptInOutStatementdoBlockStatementShortenedFunctionBody:InOutContractExpressionsopt=>AssignExpression;
Examples:
int hasSpecifiedBody() {return 1; }int hasShortenedBody() => 1;// equivalent
TheShortenedFunctionBody form implies areturn statement. This syntax also applies forfunction literals.
MissingFunctionBody:;FunctionContractsoptInOutContractExpression;FunctionContractsoptInOutStatement
Function declarations with aMissingFunctionBody, e.g.:
int foo();that are not declared asabstract are expected to have their implementations elsewhere, and that implementation will be provided at the link step. This enables an implementation of a function to be completely hidden from the user of it, and the implementation may be in another language such as C, assembler, etc. Typically a function prototype would have non-extern(D)linkage.
A function prototype can haveextern(D) linkage. This is useful forD interface files.
FunctionContracts:FunctionContractFunctionContractFunctionContractsFunctionContract:InOutContractExpressionInOutStatementInOutContractExpressions:InOutContractExpressionInOutContractExpressionInOutContractExpressionsInOutContractExpression:InContractExpressionOutContractExpressionInOutStatement:InStatementOutStatement
Function Contracts specify the preconditions and postconditions of a function. They are used inContract Programming.
Preconditions and postconditions do not affect the type of the function.
InContractExpression:in (AssertArguments)InStatement:inBlockStatement
AnInContractExpression is a precondition.
AnInStatement is also a precondition. AnyAssertExpression appearing in anInStatement will be anInContractExpression.
Preconditions must semantically be satisfied before the function starts executing. If a precondition fails, the program enters anInvalid State.
The expression form is:
in (expression)in (expression,"failure string"){ ...function body...}
The block statement form is:
in{ ...contract preconditions...}do{ ...function body...}
OutContractExpression:out ( ;AssertArguments)out (Identifier;AssertArguments)OutStatement:outBlockStatementout(Identifier)BlockStatement
AnOutContractExpression is a postcondition.
AnOutStatement is also a postcondition. AnyAssertExpression appearing in anOutStatement will be anOutContractExpression.
Postconditions must semantically be satisfied after the function finishes executing. If a postcondition fails, the program enters anInvalid State.
The expression form is:
out (identifier; expression)out (identifier; expression,"failure string")out (; expression)out (; expression,"failure string"){ ...function body...}
The block statement form is:
out{ ...contract postconditions...}out (identifier){ ...contract postconditions...}do{ ...function body...}
The optional identifier in either type of postcondition is set to the return value of the function, and can be accessed from within the postcondition. It is implicitlyconst.
int fun(refint a,int b)in (a > 0)in (b >= 0,"b cannot be negative!")out (r; r > 0,"return must be positive")out (; a != 0){// function body}
int fun(refint a,int b)in{assert(a > 0);assert(b >= 0,"b cannot be negative!");}out (r){assert(r > 0,"return must be positive");assert(a != 0);}do{// function body}
The two functions are identical semantically.
If a function in a derived class overrides a function from its super class, then only the preconditions of one of the function and its overridden functions must be satisfied. Overriding functions then becomes a process ofloosening the preconditions.
A function without preconditions means its precondition is always satisfied. Therefore if any function in an inheritance hierarchy has no preconditions, then any preconditions on functions overriding it have no meaningful effect.
Conversely, all of the postconditions of the function and its overridden functions must to be satisfied. Adding overriding functions then becomes a processes oftightening the postconditions.
Onereturn statement per execution path is required if the function specifies a return type that is notvoid, unless:
Simple forms of the first 3 cases above can be recognized by the compiler, but not all.
int error(int i){if (i & 1) i++;elsereturn i;// Error: no return statement}
int f(int i){if (i >= 0)return i;assert(0);// OK, no return needed}
Function return values not marked asref are considered to be rvalues by the calling function. This means they cannot be passed by reference to other functions.
Pure functions are annotated with thepure attribute. Pure functions cannot directly access global or static mutable state. Pure functions can only call pure functions.
Pure functions can:
int x;immutableint y;pureint foo(int i){ i++;// ok, modifying local state//x = i; // error, modifying global state//i = x; // error, reading mutable global state i = y;// ok, reading immutable global statethrownew Exception("failed");// ok}
A pure function can override an impure function, but cannot be overridden by an impure function. I.e. it is covariant with an impure function.
Aweakly pure function has parameters with mutable indirections. Program state can be modified transitively through the matching argument.
pure size_t foo(int[] arr){ arr[] += 1;return arr.length;}int[] a = [1, 2, 3];foo(a);assert(a == [2, 3, 4]);
Astrongly pure function has no parameters with mutable indirections and cannot modify any program state external to the function.
struct S {double x; }pure size_t foo(immutable(int)[] arr,int num, S val){//arr[num] = 1; // compile error num = 2;// has no side effect on the caller side val.x = 3.14;// dittoreturn arr.length;}
A strongly pure function can call a weakly pure function.
A pure function can:
A pure function can perform impure operations in statements that are in aConditionalStatement controlled by aDebugCondition.
pureint foo(int i){debug writeln("i = ", i);// ok, impure code allowed in debug statement ...}
Nested functions inside a pure function are implicitly marked as pure.
pureint foo(int x,immutableint y){int bar()// implicitly marked as pure, to be "weakly pure"// since hidden context pointer to foo stack context is mutable { x = 10;// can access states in enclosing scope// through the mutable context pointerreturn x; }pragma(msg,typeof(&bar));// int delegate() pureint baz()immutable// qualifies hidden context pointer with immutable,// and has no other parameters, therefore "strongly pure" {//return x; // error, cannot access mutable data// through the immutable context pointerreturn y;// ok }// can call pure nested functionsreturn bar() + baz();}
Apure factory function is a strongly pure function that returns a result that has only mutable indirections. All mutable memory returned by the call cannot be referenced by any other part of the program, i.e. it is newly allocated by the function. The mutable references of the result similarly cannot refer to any object that existed before the function call. This allows the result to be implicitly cast from anything toimmutable orconst shared, and fromshared andconst shared to (unshared)const. For example:
struct List {int payload; List* next; }pure List* make(int a,int b){auto result =new List(a,null); result.next =new List(b,null);return result;}void main(){auto list = make(1, 2);pragma(msg,typeof(list));// List*immutable ilist = make(1, 2);pragma(msg,typeof(ilist));// immutable List*pragma(msg,typeof(ilist.next));// immutable List*}
All references inmake's result refer toList objects created bymake, and no other part of the program refers to any of these objects. Hence the result can initialize an immutable variable.
This does not affect anyException orError thrown from the function.
int a(int)pure;// no mutable indirectionsint b(const Object)pure;// depends on argument passedimmutable(Object) c(immutable Object)pure;// always memoizablevoid g();void f(int n,const Object co,immutable Object io){constint x = a(n); g();// `n` does not change between calls to `a`int i = a(n);// same as `i = x`constint y = b(co);// `co` may have mutable indirection g();// may change fields of `co` through another reference i = b(co);// call is not memoizable, result may differconstint z = b(io); i = b(io);// same as `i = z`}
Such a function may still have behavior inconsistent with memoization by e.g. usingcasts or by changing behavior depending on the address of its parameters. An implementation is currently not required to enforce validity of memoization in all cases.
If a function throws anException or anError, the assumptions related to memoization do not carry to the thrown exception.
Pure destructors do not benefit of special elision.
Nothrow functions can only throw exceptions derived fromclass Error.
Nothrow functions are covariant with throwing ones.
Aref function returns by reference (instead of by value). The return value of aref function must be an lvalue (whereas the return value of a non-ref function can be an rvalue, too). An expression formed by calling aref function is an lvalue (whereas an expression formed by calling a non-ref function is an rvalue).
int *p;refint foo(){ p =newint(2);return *p;}void main(){int i = foo();assert(i == 2); foo() = 3;// reference returns can be lvaluesassert(*p == 3);}
Returning a reference to an expired function context is not allowed. This includes local variables, temporaries and parameters that are part of an expired function context.
refint sun(){int i;return i;// error, escaping a reference to local variable i}
Aref parameter may not be returned byref, unless it isreturn ref.
refint moon(refint i){return i;// error}
Auto functions have their return type inferred from anyReturnStatements in the function body.
An auto function is declared without a return type. Auto functions can use any validStorageClass, not justauto.
If there are multipleReturnStatements, the types of them must be implicitly convertible to a common type. If there are noReturnStatements, the return type is inferred to bevoid.
auto foo(int x) {return x + 3; }// inferred to be intpure bar(int x) {return x;return 2.5; }// inferred to be double
Auto ref functions can infer their return type just asauto functions do. In addition, they becomeref functions if all of these apply:
autoref f1(int x) {return x; }// value returnautoref f2() {return 3; }// value returnautoref f3(refint x) {return x; }// ref returnautoref f4(outint x) {return x; }// ref returnautoref f5(){staticint x;return x;// ref return}
The ref-ness of a function is determined from allReturnStatements in the function body:
autoref f1(refint x) {return 3;return x; }// ok, value returnautoref f2(refint x) {return x;return 3; }// ok, value returnautoref f3(refint x,refdouble y){return x;return y;// The return type is deduced to be double, but cast(double)x is not an lvalue,// so f3 has a value return.}
Auto ref functions can have an explicit return type.
autorefint bar(refint x) {return x; }// ok, ref returnautorefint foo(double x) {return x; }// error, cannot convert double to int
For extensive information seeinout type qualifier.
If a function call passes no explicit argument, i.e. it would syntactically use(), then these parentheses may be omitted, similar to a getter invocation of aproperty function. AUFCS call can also omit empty parentheses.
void foo() {}// no argumentsvoid fun(int x = 10) {}void bar(int[] arr) {}void main(){ foo();// OK foo;// also OK fun;// OKint[] arr; arr.bar();// UFCS call arr.bar;// also OK}
Due to ambiguity, parentheses are required to call a delegate or a function pointer:
void main(){intfunction() fp;assert(fp == 6);// Error, incompatible types int function() and intassert(*fp == 6);// Error, incompatible types int() and intintdelegate() dg;assert(dg == 6);// Error, incompatible types int delegate() and int}
If a function returns a delegate or a function pointer, any parentheses apply first to the function call, not the result. Two sets of parentheses are required to call the result directly:
int getNum() {return 6; }intfunction() getFunc() {return &getNum; }void main(){intfunction() fp; fp = getFunc;// implicit callassert(fp() == 6); fp = getFunc();// explicit callassert(fp() == 6);int x = getFunc()();assert(x == 6);}
struct S{int getNum() {return 6; }intdelegate() getDel()return {return &getNum; }}void main(){ S s;intdelegate() dg; dg = s.getDel;// implicit callassert(dg() == 6); dg = s.getDel();// explicit callassert(dg() == 6);int y = s.getDel()();assert(y == 6);}
WARNING: The definition and usefulness of property functions is being reviewed, and the implementation is currently incomplete. Using property functions is not recommended until the definition is more certain and implementation more mature.
Properties are functions that can be syntactically treated as if they were fields or variables. Properties can be read from or written to. A property is read by calling a method or function with no arguments; a property is written by calling a method or function with its argument being the value it is set to.
Simple getter and setter properties can be written usingUFCS. These can be enhanced with the additon of the@property attribute to the function, which adds the following behaviors:
A simple property would be:
struct Foo{ @propertyint data() {return m_data; }// read property @propertyint data(int value) {return m_data = value; }// write propertyprivate:int m_data;}
To use it:
int test(){ Foo f; f.data = 3;// same as f.data(3);return f.data + 3;// same as return f.data() + 3;}
The absence of a read method means that the property is write-only. The absence of a write method means that the property is read-only. Multiple write methods can exist; the correct one is selected using the usual function overloading rules.
In all the other respects, these methods are like any other methods. They can be static, have different linkages, have their address taken, etc.
The built in properties.sizeof,.alignof, and.mangleof may not be declared as fields or methods in structs, unions, classes or enums.
If a property function has no parameters, it works as a getter. If has exactly one parameter, it works as a setter.
Virtual functions are class member functions that are called indirectly through a function pointer table, called avtbl[], rather than directly. Member functions that are virtual can be overridden in a derived class:
class A{void foo(int x) {}}class B : A{overridevoid foo(int x) {}//override void foo() {} // error, no foo() in A}void test(){ A a =new B(); a.foo(1);// calls B.foo(int)}
Theoverride attribute is required when overriding a function. This is useful for catching errors when a base class's member function has its parameters changed, and all derived classes need to have their overriding functions updated.
Thefinal method attribute prevents a subclass from overriding the method.
The following are not virtual:
Example:
class A{int def() { ... }finalint foo() { ... }finalprivateint bar() { ... }privateint abc() { ... }}class B : A{overrideint def() { ... }// ok, overrides A.defoverrideint foo() { ... }// error, A.foo is finalint bar() { ... }// ok, A.bar is final private, but not virtualint abc() { ... }// ok, A.abc is not virtual, B.abc is virtual}void test(){ A a =new B; a.def();// calls B.def a.foo();// calls A.foo a.bar();// calls A.bar a.abc();// calls A.abc}
Member functions withObjective-C linkage are virtual even if marked withfinal orstatic, and can be overridden.
An overriding function may be covariant with the overridden function. A covariant function has a type that is implicitly convertible to the type of the overridden function.
class A { }class B : A { }class Foo{ A test() {returnnull; }}class Bar : Foo{// overrides and is covariant with Foo.test()override B test() {returnnull; }}
To directly call a member function of a base classBase, writeBase. before the function name. This avoids dynamic dispatch through a function pointer. For example:
class B{int foo() {return 1; }}class C : B{overrideint foo() {return 2; }void test() {assert(B.foo() == 1);// translated to this.B.foo(), and// calls B.foo statically.assert(C.foo() == 2);// calls C.foo statically, even if// the actual instance of 'this' is D. }}class D : C{overrideint foo() {return 3; }}void main(){auto d =new D();assert(d.foo() == 3);// calls D.fooassert(d.B.foo() == 1);// calls B.fooassert(d.C.foo() == 2);// calls C.foo d.test();}
Base class methods can also be called through thesuper reference.
When doing overload resolution, the functions in the base class are not considered, as they are not in the sameOverload Set:
class A{int foo(int x) { ... }int foo(long y) { ... }}class B : A{overrideint foo(long x) { ... }}void test(){ B b =new B(); b.foo(1);// calls B.foo(long), since A.foo(int) is not considered A a = b; a.foo(1);// issues runtime error (instead of calling A.foo(int))}
To include the base class's functions in the overload resolution process, use anAliasDeclaration:
class A{int foo(int x) { ... }int foo(long y) { ... }}class B : A{alias foo = A.foo;overrideint foo(long x) { ... }}void test(){ A a =new B(); a.foo(1);// calls A.foo(int) B b =new B(); b.foo(1);// calls A.foo(int)}
If such anAliasDeclaration is not used, the derived class's functions completely override all the functions of the same name in the base class, even if the types of the parameters in the base class functions are different. It is illegal if, through implicit conversions to the base class, those other functions do get called:
class A{voidset(long i) { }void set(int i) { }}class B : A{overridevoid set(long i) { }}void test(){ A a =new B; a.set(3);// error, use of A.set(int) is hidden by B// use 'alias set = A.set;' to introduce base class overload set}
A function parameter's default value is not inherited:
class A{voidfoo(int x = 5) { ... }}class B : A{void foo(intx = 7) { ... }}class C : B{void foo(intx) { ... }}void test(){ A a =new A(); a.foo();// calls A.foo(5) B b =new B(); b.foo();// calls B.foo(7) C c =new C(); c.foo();// error, need an argument for C.foo}
An overriding function inherits any unspecifiedFunctionAttributes from the attributes of the overridden function.
class B{void foo()purenothrow @safe {}}class D : B{overridevoid foo() {}}void main(){auto d =new D();pragma(msg,typeof(&d.foo));// prints "void delegate() pure nothrow @safe" in compile time}
The attributes@disable anddeprecated are not allowed on overriding functions.
class B{void foo() {}}class D : B{ @disableoverridevoid foo() {}// error, can't apply @disable to overriding function}
The compiler makes the decision whether to inline a function or not. This decision may be controlled bypragma(inline).
Function overloading occurs when two or more functions in the same scope have the same name. The function selected is the one that is thebest match to the arguments. The matching levels are:
Named arguments are resolved for a candidate according toMatching Arguments to Parameters. If this fails (for example, because the overload does not have a parameter matching a named argument), the level isno match. Other than that, named arguments do not affect the matching level.
Each argument (including anythis reference) is compared against the function's corresponding parameter to determine the match level for that argument. The match level for a function is theworst match level of each of its arguments.
Literals do not matchref orout parameters.
scope parameter storage class does not affect function overloading.
If two or more functions have the same match level, thenpartial ordering is used to disambiguate to find the best match. Partial ordering finds the most specialized function. If neither function is more specialized than the other, then it is an ambiguity error. Partial ordering is determined for functionsf andg by taking the parameter types off, constructing a list of arguments by taking the default values of those types, and attempting to match them againstg. If it succeeds, theng is at least as specialized asf. For example:
class A { }class B : A { }class C : B { }void foo(A);void foo(B);void test(){ C c;/* Both foo(A) and foo(B) match with implicit conversions (level 2). * Applying partial ordering rules, * foo(B) cannot be called with an A, and foo(A) can be called * with a B. Therefore, foo(B) is more specialized, and is selected. */ foo(c);// calls foo(B)}
A function with a variadic argument is considered less specialized than a function without.
A static member function can be overloaded with a member function. The struct, class or union of the static member function is inferred from the type of thethis argument.
struct S {void eggs(int);staticvoid eggs(long);}S s;s.eggs(0);// calls void eggs(int);S.eggs(0);// error: need `this`s.eggs(0L);// calls static void eggs(long);S.eggs(0L);// calls static void eggs(long);struct T {void bacon(int);staticvoid bacon(int);}T t;t.bacon(0);// error: ambiguousT.bacon(0);// error: ambiguous
Functions declared at the same scope overload against each other, and are called anOverload Set. An example of an overload set are functions defined at module level:
module A;void foo() { }void foo(long i) { }
A.foo() andA.foo(long) form an overload set. A different module can also define another overload set of functions with the same name:
module B;class C { }void foo(C) { }void foo(int i) { }
and A and B can be imported by a third module, C. Both overload sets, theA.foo overload set and theB.foo overload set, are found when searching for symbolfoo. An instance offoo is selected based on it matching in exactly one overload set:
import A;import B;void bar(C c ,long i){ foo();// calls A.foo() foo(i);// calls A.foo(long) foo(c);// calls B.foo(C) foo(1,2);// error, does not match any foo foo(1);// error, matches A.foo(long) and B.foo(int) A.foo(1);// calls A.foo(long)}
Even thoughB.foo(int) is a better match than A.foo(long) forfoo(1), it is an error because the two matches are in different overload sets.
Overload sets can be merged with an alias declaration:
import A;import B;alias foo = A.foo;alias foo = B.foo;void bar(C c){ foo();// calls A.foo() foo(1L);// calls A.foo(long) foo(c);// calls B.foo(C) foo(1,2);// error, does not match any foo foo(1);// calls B.foo(int) A.foo(1);// calls A.foo(long)}
Parameter storage classes arein,out,ref,lazy,return andscope. Parameters can also take the type constructorsconst,immutable,shared andinout.
in,out,ref andlazy are mutually exclusive. The first three are used to denote input, output and input/output parameters, respectively. For example:
int read(inchar[] input,ref size_t count,outint errno);void main(){ size_t a = 42;int b;int r = read("Hello World", a, b);}
read has three parameters.input will only be read and no reference to it will be retained.count may be read and written to, anderrno will be set to a value from within the function.
The argument"Hello World" gets bound to parameterinput,a gets bound tocount andb toerrno.
| Storage Class | Description |
|---|---|
| none | The parameter will be a mutable copy of its argument. |
| in | The parameter is an input to the function. |
| out | The argument must be an lvalue, which will be passed by reference and initialized upon function entry with the default value (T.init) of its type. |
| ref | The parameter is aninput/output parameter, passed by reference. |
| scope | The parameter must not escape the function call (e.g. by being assigned to a global variable). Ignored for any parameter that is not a reference type. |
| return | Parameter may be returned or copied to the first parameter, but otherwise does not escape from the function. Such copies are required not to outlive the argument(s) they were derived from. Ignored for parameters with no references. SeeScope Parameters. |
| lazy | argument is evaluated by the called function and not by the caller |
| Type Constructor | Description |
| const | argument is implicitly converted to a const type |
| immutable | argument is implicitly converted to an immutable type |
| shared | argument is implicitly converted to a shared type |
| inout | argument is implicitly converted to an inout type |
The parameter is an input to the function. Input parameters behave as if they have theconst storage class.
in parameters also behave likescope parameters.
const(int[]) g(inint[] a) @safe{ a[0]++;// Error: cannot modify const expressionreturn a;// Error: scope parameter `a` may not be returned}
Input parameters may also be passed by reference by the compiler. Unlikeref parameters,in parameters can bind to both lvalues and rvalues (such as literals).
By default, parameters take rvalue arguments. Aref parameter takes an lvalue argument, so changes to its value will operate on the caller's argument.
void inc(refint x){ x += 1;}void seattle(){int z = 3; inc(z);assert(z == 4);}
Aref parameter can also be returned by reference, seeReturn Ref Parameters.
Anout parameter is similar to aref parameter, except it is initialized bydefault construction upon function invocation.
void zero(outint x){assert(x == 0);}void two(outint x){ x = 2;}void tacoma(){int a = 3; zero(a);assert(a == 0);int y = 3; two(y);assert(y == 2);}
For dynamic array and class object parameters, which are always passed by reference,out andref apply only to the reference and not the contents.
An argument to alazy parameter is not evaluated before the function is called. The argument is only evaluated if/when the parameter is evaluated within the function. Hence, alazy argument can be executed 0 or more times.
import std.stdio : writeln;void main(){int x; 3.times(writeln(x++)); writeln("-"); writeln(x);}void times(int n,lazyvoid exp){while (n--) exp();}
prints to the console:
012−3
Alazy parameter cannot be an lvalue.
The underlying delegate of thelazy parameter may be extracted by using the& operator:
void test(lazyint dg){intdelegate() dg_ = &dg;assert(dg_() == 7);assert(dg == dg_());}void main(){int a = 7; test(a);}
Alazy parameter of typevoid can accept an argument of any type.
See Also:Lazy Variadic Functions
Function parameter declarations can have default values:
void foo(int x,int y = 3){ ...}...foo(4);// same as foo(4, 3);
Default parameters are resolved and semantically checked in the context of the function declaration.
module m;privateimmutableint b;purevoid g(int a = b) {}
import m;int b;purevoid f(){ g();// ok, uses m.b}
The attributes of theAssignExpression are applied where the default expression is used.
module m;int b;purevoid g(int a = b) {}
import m;enumint b = 3;purevoid f(){ g();// error, cannot access mutable global `m.b` in pure function}
See also: function type aliaseswith default values.
Return ref parameters are used withref functions to ensure that the returned reference will not outlive the matching argument's lifetime.
refint identity(returnrefint x) {return x;// pass-through function that does nothing}refint fun() {int x;return identity(x);// Error: escaping reference to local variable x}refint gun(returnrefint x) {return identity(x);// OK}
Returning the address of aref variable is also checked in@safe code.
int* pluto(refint i) @safe{return &i;// error: returning &i escapes a reference to parameter i}int* mars(returnrefint i) @safe{return &i;// OK with -preview=dip1000}
void f(refscopeint* p,returnrefint i) @safe{ p = &i;// OK with -preview=dip1000}void main() @safe{int i;int* p; f(p, i);// OK, lifetime of p is shorter than i *p = 5;assert(i == 5);int j;//f(p, j); // error, lifetime of p is longer than j}Thethis reference parameter to a struct non-static member function isconsidered the first parameter.
struct S{privateint* p;void f(returnrefint i)scope @safe { p = &i;// OK with -preview=dip1000 }}void main() @safe{int i; S s; s.f(i);// OK, lifetime of `s` is shorter than `i` *s.p = 2;assert(i == 2);}
If there are multiplereturn ref parameters, the lifetime of the returnvalue is the smallest lifetime of the corresponding arguments.
Neither the type of thereturn ref parameter(s) nor the type of the returnvalue is considered when determining the lifetime of the return value.
It is not an error if the return type does not contain any indirections.
int mercury(returnrefint i){return i;// ok}
Template functions, auto functions, nested functions andlambdas can deduce thereturn attribute.
@safe:refint templateFunction()(refint i){return i;// ok}refauto autoFunction(refint i){return i;// ok}void uranus(){refint nestedFunction(refint i) {return i;// ok }auto lambdaFunction = (refint i) {return &i;// ok };}
Struct non-static methods can be marked with thereturn attribute to ensure a returned reference will not outlive the struct instance.
struct S{privateint x;refint get()return {return x; }}refint escape(){ S s;return s.get();// Error: escaping reference to local variable s}
The hiddenthis ref-parameter then becomesreturn ref.
Thereturn attribute can also be used to limit the lifetime of the returned value, even when the method is notref:
struct S{privateint i;int* get()return @safe => &i;}void f() @safe{int* p; { S s;int *q = s.get();// OK, q has shorter lifetime than s p = s.get();// error, p has longer lifetime p = (new S).get();// OK, heap allocated S }}
Ascope parameter of reference type must not escape the function call (e.g. by being assigned to a global variable). It has no effect for non-reference types.scope escape analysis is only done for@safe functions. For other functionsscope semantics must be manually enforced.
@safe:int* gp;void thorin(scopeint*);void gloin(int*);int* balin(scopeint* q,int* r){ gp = q;// error, q escapes to global gp gp = r;// ok thorin(q);// ok, q does not escape thorin() thorin(r);// ok gloin(q);// error, gloin() escapes q gloin(r);// ok that gloin() escapes rreturn q;// error, cannot return 'scope' qreturn r;// ok}
Parameters marked asreturn scope that contain indirections can only escape those indirections via the function's return value.
@safe:int* gp;void thorin(scopeint*);void gloin(int*);int* balin(returnscopeint* p){ gp = p;// error, p escapes to global gp thorin(p);// ok, p does not escape thorin() gloin(p);// error, gloin() escapes preturn p;// ok}
Class references are considered pointers that are subject toscope.
@safe:class C { }C gp;void thorin(scope C);void gloin(C);C balin(returnscope C p,scope C q, C r){ gp = p;// error, p escapes to global gp gp = q;// error, q escapes to global gp gp = r;// ok thorin(p);// ok, p does not escape thorin() thorin(q);// ok thorin(r);// ok gloin(p);// error, gloin() escapes p gloin(q);// error, gloin() escapes q gloin(r);// ok that gloin() escapes rreturn p;// okreturn q;// error, cannot return 'scope' qreturn r;// ok}
return scope can be applied to thethis of class and interface member functions.
class C{ C bofur()returnscope {returnthis; }}
Template functions, auto functions, nested functions andlambdas can deduce thereturn scope attribute.
It is not possible to have bothreturn ref andreturn scope semantics for the same parameter. When a parameter is passed byref and has both thereturn andscope storage classes, it getsreturn scope semantics if and only if thereturn andscope keywords appear adjacent to each other, in that order. Specifying areturn ref andscope parameter enables returning a reference to a scope pointer. In all other cases, the parameter hasreturn ref semantics and regularscope semantics.
U xerxes(refreturnscope V v)// (1) ref and return scopeU sargon(returnrefscope V v)// (2) return ref and scopestruct S{// note: in struct member functions, the implicit `this` parameter// is passed by `ref` U xerxes()returnscope;// return scope U sargon()scopereturn;// return ref, `return` comes after `scope` U xerxes()returnconstscope;// return ref, `return` and `scope` are not adjacent}
Example of combinations ofreturn scope,return ref, andscope semantics:
@safe:int* globalPtr;struct S{int val;int* ptr;this(returnscoperefint* p) { ptr = p; }// note: `this` is passed by `ref` in structsint* retRefA()scopereturn// return-ref, scope { globalPtr =this.ptr;// disallowed, `this` is `scope`return &this.val;// allowed, `return` means `return ref` }refint retRefB()scopereturn// return-ref, scope { globalPtr =this.ptr;// disallowed, `this` is `scope`returnthis.val;// allowed, `return` means `return ref` }int* retScopeA()returnscope// ref, return-scope {return &this.val;// disallowed, escaping a reference to `this`returnthis.ptr;// allowed, returning a `return scope` pointer }refint retScopeB()returnscope// ref, return-scope {returnthis.val;// disallowed, escaping a reference to `this`return *this.ptr;// allowed, returning a `return scope` pointer }refint* retRefScopeC()scopereturn// return-ref, scope {returnthis.ptr;// allowed, returning a reference to a scope pointer }}int* retRefA(returnrefscope S s){ globalPtr = s.ptr;// disallowed, `s` is `scope`return &s.val;// allowed, returning a reference to `return ref s`}refint retRefB(returnrefscope S s){ globalPtr = s.ptr;// disallowed, `s` is `scope`return s.val;}int* retScopeA(refreturnscope S s){return &s.val;// disallowed, escaping a reference to `s`return s.ptr;// allowed, returning a `return scope` pointer}refint retScopeB(refreturnscope S s){return s.val;// disallowed, escaping a reference to `s`return *s.ptr;// allowed, returning a `return scope` pointer}refint* retRefScopeC(returnrefscopeint* p){return p;// allowed, returning a reference to a scope pointer}
@safe:int dereference(int* x)purenothrow;int* identity(int* x)purenothrow;int* identityThrow(int* x)pure;void assignToRef(int* x,refint* escapeHatch)purenothrow;void assignToPtr(int* x,int** escapeHatch)purenothrow;void cannotAssignTo(int* x,constrefint* noEscapeHatch)purenothrow;int* globalPtr;int* test(scopeint* ptr){int result = dereference(ptr);// allowed, treated as `scope`int* ptr2 = identity(ptr);// allowed, treated as `return scope`int* ptr3 = identityThrow(ptr);// not allowed, can throw an `Exception` assignToRef(ptr, globalPtr);// not allowed, mutable second parameter assignToPtr(ptr, &globalPtr);// not allowed, mutable second parameter cannotAssignTo(ptr, globalPtr);// allowedreturn ptr2;// not allowed, ptr2 is inferred `scope` now}
Variadic Functions take a variable number of arguments. There are three forms:
A C-style variadic function is declared with a parameter... as the last function parameter. It has non-D linkage, such asextern (C).
extern (C)void dry(int x,int y, ...);// C-style Variadic Functionvoid spin(){ dry(3, 4);// ok, no variadic arguments dry(3, 4, 6.8);// ok, one variadic argument//dry(2); // error, no argument for parameter y}
There must be at least one non-variadic parameter declared.
extern (C)int def(...);// error, must have at least one parameter
C-style variadic functions match the C calling convention for variadic functions, and can call C Standard library functions likeprintf.
extern (C)int printf(const(char)*, ...);void main(){ printf("hello world\n");}
C-style variadic functions cannot be marked as@safe.
To access the variadic arguments, import the standard library modulecore.stdc.stdarg.
import core.stdc.stdarg;import std.stdio;extern (C)void rinse(int x,int y, ...){ va_list args; va_start(args, y);// y is the last named parameterint z; va_arg(args, z);// z is set to 5 va_end(args); writeln(z);}void main(){ rinse(3, 4, 5);// first variadic argument is 5}
D-style variadic functions have D linkage and... as the last parameter.
... can be the only parameter.
If there are parameters preceding the... parameter, there must be a comma separating them from the....
int abc(char c, ...);// one required parameter: cint def(...);// no required parametersint ghi(int i ...);// a typesafe variadic function//int boo(, ...); // error
Two hidden arguments are passed to the function:
_argptr is a pointer to the first of the variadic arguments. To access the variadic arguments, importcore.vararg. Use_argptr in conjunction withva_arg:
import core.vararg;import std.stdio;@systemvoid foo(int x,int y, ...){int z = va_arg!int(_argptr);// z is set to 5 and _argptr is advanced// to the next argument writeln(z);// 5}void main(){ foo(3, 4, 5);// first variadic argument is 5}
_arguments gives the number of arguments and thetypeid of each, enabling type safety to be checked at run time.
import std.stdio;void main(){ Foo f =new Foo(); Bar b =new Bar(); writefln("%s", f); printargs(1, 2, 3L, 4.5, f, b);}class Foo {int x = 3; }class Bar {long y = 4; }import core.vararg;@systemvoid printargs(int x, ...){ writefln("%d arguments", _arguments.length);for (int i = 0; i < _arguments.length; i++) { writeln(_arguments[i]);if (_arguments[i] ==typeid(int)) {int j = va_arg!(int)(_argptr); writefln("\t%d", j); }elseif (_arguments[i] ==typeid(long)) {long j = va_arg!(long)(_argptr); writefln("\t%d", j); }elseif (_arguments[i] ==typeid(double)) {double d = va_arg!(double)(_argptr); writefln("\t%g", d); }elseif (_arguments[i] ==typeid(Foo)) { Foo f = va_arg!(Foo)(_argptr); writefln("\t%d", f.x); }elseif (_arguments[i] ==typeid(Bar)) { Bar b = va_arg!(Bar)(_argptr); writefln("\t%d", b.y); }elseassert(0); }}
0x00870FE05 argumentsint 2long 3double 4.5Foo 3Bar 4
D-style variadic functions cannot be marked as@safe (if they callva_arg).
A typesafe variadic function has D linkage and a variadic parameter, which is typically an array followed by.... The variadic parameter must be the last one. When passing component arguments (e.g. array elements), the variadic parameter is implicitly constructed from the given arguments.
A dynamic array variadic parameter accepts either a dynamic array argument or any number of arguments, each of which must implicitly convert to the array element type:
int sum(int[] ar ...)// typesafe variadic function{int s;foreach (int x; ar) s += x;return s;}void main(){assert(sum(1, 2, 3) == 6);assert(sum() == 0);int[3] ii = [4, 5, 6];assert(sum(ii) == 15);}
A static array variadic parameter accepts either a static array argument or a fixed number of arguments which matches the array dimension:
int sum(int[3] ar ...)// typesafe variadic function{int s;foreach (int x; ar) s += x;return s;}void main(){int i;//i = sum(2, 3); // error, need 3 values for array i = sum(1, 2, 3);assert(i == 6);int[3] ii = [4, 5, 6]; i = sum(ii);assert(i == 15);int[] jj = ii;//i = sum(jj); // error, type mismatch}
int tesla(int x, C c ...){return x + c.x;}class C{int x; string s;this(int x, string s) {this.x = x;this.s = s; }}void edison(){ C g =new C(3,"abc"); tesla(1, c);// ok, since c is an instance of C tesla(1, 4,"def");// ok tesla(1, 5);// error, no matching constructor for C}
The lifetime of the variadic class object or array instance ends at the end of the function.
C orville(C c ...){return c;// error, c instance contents invalid after return}int[] wilbur(int[] a ...){return a;// error, array contents invalid after returnreturn a[0..1];// error, array contents invalid after returnreturn a.dup;// ok, since copy is made}For other types, the variadic parameter matches exactly one argument, which is passed by value.
int neil(int i ...){return i;}void buzz(){ neil();// error, missing argument neil(3);// returns 3 neil(3, 4);// error, too many argumentsint[] x; neil(x);// error, type mismatch}
If the variadic parameter of a function is an array of delegates with no parameters, then each of the arguments whose type does not match that of the delegate is converted to a delegate of that type.
void hal(scopeintdelegate()[] dgs ...);void dave(){intdelegate() dg; hal(1, 3+x, dg,cast(intdelegate())null);// (1) hal( {return 1; }, {return 3+x; }, dg,null );// same as (1)}
The variadic delegate array differs from using a lazy variadic array. With the latter each array element access would evaluate every array element. With the former, only the element being accessed would be evaluated.
import std.stdio;void main(){int x; ming(++x, ++x);int y; flash(++y, ++y);}// lazy variadic arrayvoid ming(lazyint[] arr...){ writeln(arr[0]);// 1 writeln(arr[1]);// 4}// variadic delegate arrayvoid flash(scopeintdelegate()[] arr ...){ writeln(arr[0]());// 1 writeln(arr[1]());// 2}
| Term | Description |
|---|---|
| I | type that contains no indirections |
| P | type that contains indirections |
| X | type that may or may not contain indirections |
| p | parameter of type P |
| i | parameter of type I |
| ref | ref orout parameter |
| returned | returned via thereturn statement |
| escaped | stored in a global or other memory not in the function’s stack frame |
A parameter must be in one of the following states:
| Term | Description |
|---|---|
| None | p may be returned or escaped |
| ReturnScope | p may be returned but not escaped |
| Scope | p may be neither returned nor escaped |
| Ref | p may be returned or escaped,ref may not be returned nor escaped |
| ReturnRef | p may be returned or escaped,ref may be returned but not escaped |
| RefScope | p may be neither returned nor escaped,ref may not be returned nor escaped |
| ReturnRef-Scope | p may be neither returned nor escaped,ref may be returned but not escaped |
| Ref-ReturnScope | p may be returned but not escaped,ref may not be returned nor escaped |
| ReturnRef-ReturnScope | p may be returned but not escaped,ref may be returned but not escaped. This isn't expressible with the current syntax and so is not allowed. |
The juxtaposition ofreturn immediately precedingscope means ReturnScope. Otherwise,return andref in any position means ReturnRef.
| Example | Classification | Comments |
|---|---|---|
| X foo(P p) | None | |
| X foo(scope P p) | Scope | |
| P foo(return scope P p) | ReturnScope | |
| I foo(return scope P p) | Scope | Thereturn is dropped because the return typeI contains no pointers. |
| P foo(return P p) | ReturnScope | Makes no sense to havereturn withoutscope. |
| I foo(return P p) | Scope | Thereturn is dropped because the return typeI contains no pointers. |
| X foo(ref P p) | Ref | |
| X foo(ref scope P p) | RefScope | |
| P foo(ref return scope P p) | Ref-ReturnScope | |
| P foo(return ref scope P p) | ReturnRef-Scope | |
| I foo(ref return scope P p) | RefScope | |
| P foo(ref return P p) | ReturnRef | |
| I foo(ref return P p) | Ref | |
| ref X foo(P p) | None | |
| ref X foo(scope P p) | Scope | |
| ref X foo(return scope P p) | ReturnScope | |
| ref X foo(return P p) | ReturnScope | Makes no sense to havereturn withoutscope. |
| ref X foo(ref P p) | Ref | |
| ref X foo(ref scope P p) | RefScope | |
| ref X foo(ref return scope P p) | Ref-ReturnScope | |
| ref X foo(return ref scope P p) | ReturnRef-Scope | |
| ref X foo(ref return P p) | ReturnRef | |
| X foo(I i) | None | |
| X foo(scope I i) | None | |
| X foo(return scope I i) | None | |
| X foo(return I i) | None | |
| X foo(ref I i) | Ref | |
| X foo(ref scope I i) | Ref | |
| X foo(ref return scope I i) | Ref | |
| P foo(ref return I i) | ReturnRef | |
| I foo(ref return I i) | Ref | |
| ref X foo(I i) | None | |
| ref X foo(scope I i) | None | |
| ref X foo(return scope I i) | None | |
| ref X foo(return I i) | None | |
| ref X foo(ref I i) | Ref | |
| ref X foo(ref scope I i) | Ref | |
| ref X foo(ref return scope I i) | ReturnRef | |
| ref X foo(ref return I i) | ReturnRef |
Member functions are rewritten as if thethis parameter is the first parameter of a non-member function,
struct S { X foo();}is treated as:
X foo(ref S);and:
class C { X foo()}is treated as:
X foo(P)
The rules account for switching betweenref and P, such as:
int* foo(returnrefint i) {return &i; }refint foo(int* p) {return *p; }
Covariance means a parameter with restrictions can be converted to a parameter with fewer restrictions. This is deducible from the description of each state.
| From\To | None | ReturnScope | Scope |
|---|---|---|---|
| None | ✔ | ||
| ReturnScope | ✔ | ✔ | |
| Scope | ✔ | ✔ | ✔ |
| From\To | Ref | ReturnRef | RefScope | ReturnRef-Scope | Ref-ReturnScope |
|---|---|---|---|---|---|
| Ref | ✔ | ✔ | |||
| ReturnRef | ✔ | ||||
| RefScope | ✔ | ✔ | ✔ | ✔ | ✔ |
| ReturnRef-Scope | ✔ | ✔ | |||
| Ref-ReturnScope | ✔ | ✔ | ✔ |
For example,scope matches all non-ref parameters, andref scope matches all ref parameters.
Local variables are declared within the scope of a function. Function parameters are included.
A local variable cannot be read without first assigning it a value.
The address of or reference to a local non-static variable cannot be returned from the function.
A local variable and a label in the same function cannot have the same name.
A local variable cannot hide another local variable in the same function.
refdouble func(int x){int x;// error, hides previous definition of xdouble y; {char y;// error, hides previous definition of yint z; } {wchar z;// Ok, previous z is out of scope } z:// error, z is a local variable and a labelreturn y;// error, returning ref to local}
Local variables in functions declared asstatic,shared static or__gshared are statically allocated rather than being allocated on the stack. The lifetime of__gshared andshared static variables begins when the function is first executed and ends when the program ends. The lifetime ofstatic variables begins when the function is first executed within the thread and ends when that thread terminates.
import std.stdio : writeln;void foo(){staticint n;if (++n == 100) writeln("called 100 times");}
The initializer for a static variable must be evaluatable at compile time. There are no static constructors or static destructors for static local variables.
Although static variable name visibility follows the usual scoping rules, the names of them must be unique within a particular function.
void main(){ {staticint x; } {staticint x; }// error {int i; } {int i; }// ok}
Functions may be nested within other functions:
int bar(int a){int foo(int b) {int abc() {return 1; }return b + abc(); }return foo(a);}void test(){int i = bar(3);// i is assigned 4}
Nested functions can be accessed only if the name is in scope.
void foo(){void A() { B();// error, B() is forward referenced C();// error, C undefined }void B() { A();// ok, in scopevoid C() {void D() { A();// ok B();// ok C();// ok D();// ok } } } A();// ok B();// ok C();// error, C undefined}and:
int bar(int a){int foo(int b) {return b + 1; }int abc(int b) {return foo(b); }// okreturn foo(a);}void test(){int i = bar(3);// okint j = bar.foo(3);// error, bar.foo not visible}
Nested functions have access to the variables and other symbols defined by the lexically enclosing function. This access includes both the ability to read and write them.
int bar(int a){int c = 3;int foo(int b) { b += c;// 4 is added to b c++;// bar.c is now 5return b + c;// 12 is returned } c = 4;int i = foo(a);// i is set to 12return i + c;// returns 17}void test(){int i = bar(3);// i is assigned 17}
This access can span multiple nesting levels:
int bar(int a){int c = 3;int foo(int b) {int abc() {return c;// access bar.c }return b + c + abc(); }return foo(3);}
Static nested functions cannot access any stack variables of any lexically enclosing function, but can access static variables. This is analogous to how static member functions behave.
int bar(int a){int c;staticint d;staticint foo(int b) { b = d;// ok b = c;// error, foo() cannot access frame of bar()return b + 1; }return foo(a);}
Functions can be nested within member functions:
struct Foo{int a;int bar() {int c;int foo() {return c + a; }return 0; }}
Nested functions always have the D function linkage type.
Unlike module level declarations, declarations within function scope are processed in order. This means that two nested functions cannot mutually call each other:
void test(){void foo() { bar(); }// error, bar not definedvoid bar() { foo(); }// ok}
There are several workarounds for this limitation:
void test(){staticstruct S {staticvoid foo() { bar(); }// okstaticvoid bar() { foo(); }// ok } S.foo();// compiles (but note the infinite runtime loop)}
void test(){void foo()() { bar(); }// ok (foo is a function template)void bar() { foo(); }// ok}
mixintemplate T(){void foo() { bar(); }// okvoid bar() { foo(); }// ok}void main(){mixin T!();}
void test(){voiddelegate() fp;void foo() { fp(); }void bar() { foo(); } fp = &bar;}
Nested functions cannot be overloaded.
A function pointer is declared with thefunction keyword:
void f(int);voidfunction(int) fp = &f;// fp is a pointer to a function taking an int
A function pointer can point to a static nested function:
intfunction() fp;// fp is a pointer to a function returning an intvoid test(){staticint a = 7;staticint foo() {return a + 3; } fp = &foo;}void main(){assert(!fp); test();int i = fp();assert(i == 10);}
int abc(int x) {return x + 1; }uint def(uint y) {return y + 1; }intfunction(int) fp1 = &abc;uintfunction(uint) fp2 = &def;// Do not rely on fp1 and fp2 being different values; the compiler may merge// them.
Adelegate can be set to a non-staticnested function:
intdelegate() dg;void test(){int a = 7;int foo() {return a + 3; } dg = &foo;int i = dg();// i is set to 10}void main(){ test();int i = dg();// ok, test.a is in a closure and still existsassert(i == 10);}
The stack variables referenced by a nested function are still valid even after the function exits (NOTE this is different from D 1.0). This combining of the environment and the function is called adynamic closure.
Those referenced stack variables that make up the closure are allocated on the GC heap, unless:
@nogc:void f(scopeintdelegate());void g(intdelegate());void main(){int i;int h() {return i; } h();// OKscope x = &h;// OK x();// OK//auto y = &h; // error, can't allocate closure in @nogc function f(&h);// OK//g(&h); // error// delegate literals f(() => i);// OKscope d = () => i;// OK d = () => i + 1;// OK f(d);//g(() => i); // error, can't allocate closure in @nogc function}
Delegates to non-static nested functions contain two pieces of data: the pointer to the stack frame of the lexically enclosing function (called thecontext pointer) and the address of the function. This is analogous to struct/class non-static member function delegates consisting of athis pointer and the address of the member function. Both forms of delegates are indistinguishable, and are the same type.
A delegate can be set to a particular object and method using&obj.method:
struct Foo{int a;int get() {return a; }}int add1(intdelegate() dg){return dg() + 1;}void main(){ Foo f = {7};intdelegate() dg = &f.get;// bind to an instance of Foo and a methodint i = add1(dg);assert(i == 8);int x = 27;int abc() {return x; } i = add1(&abc);assert(i == 28);}
Function pointers are zero-initialized by default. They can be initialized to the address of any function (including a function literal). Initialization with the address of a function that requires a context pointer is not allowed in @safe functions.
struct S{staticint sfunc();int member();// has hidden `this` reference parameter}@safevoid sun(){intfunction() fp = &S.sfunc; fp();// Ok fp = &S.member;// error}@systemvoid moon(){intfunction() fp = &S.member;// Ok because @system fp();// undefined behavior}
Delegates are zero-initialized by default. They can be initialized by taking the address of a non-static member function, but a context pointer must be supplied. They can be initialized by taking the address of a non-static nested function or function literal, where the context pointer will be set to point to the stack frame, closure, ornull.
Delegates cannot be initialized by taking the address of a global function, a static member function, or a static nested function.
struct S{staticint sfunc();int member() {return 1; }}void main(){ S s;intdelegate() dg = &s.member;// Ok, s supplies context pointerassert(dg() == 1);//dg = &S.sfunc; // error//dg = &S.member; // errorint moon() {return 2; } dg = &moon;// Okassert(dg() == 2);staticint mars() {return 3; }//dg = &mars; // error dg = () {return 4; };// Okassert(dg() == 4);}
The last assignment uses aFunctionLiteral, whichis inferred as a delegate.
SeeFunctionLiterals.
For console programs,main() serves as the entry point. It gets called after all themodule initializers are run, and after anyunittests are run. After it returns, all the module destructors are run.main() must be declared as follows:
MainFunction:MainReturnDeclmain()FunctionBodyMainReturnDeclmain(string[]Identifier)FunctionBodyMainReturnDecl:voidintnoreturnauto
If thestring[] parameter is declared, the parameter will hold arguments passed to the program by the OS. The first argument is typically the executable name, followed by any command-line arguments.
The main function must have D linkage.
Attributes may be added as needed, e.g.@safe,@nogc,nothrow, etc.
Programs may define anextern(C) main function as an alternative to the standardentry point. This form is required forBetterC programs.
A Cmain function must be declared as follows:
CMainFunction:extern (C)MainReturnDeclmain(CmainParametersopt)BlockStatementCmainParameters:intIdentifier,char**IdentifierintIdentifier,char**Identifier,char**Identifier
When defined, the first two parameters denote a C-style array (length + pointer) that holds the arguments passed to the program by the OS. The third parameter is a POSIX extension calledenviron and holds information about the current environment variables.
This function takes the place of the C main function and is executed immediately without any setup or teardown associated with a Dmain function. Programs reliant on module constructors, module destructors, or unittests need to manually perform (de)initialization using the appropriateruntime functions.
pragma(mangle,"main")int myMain(int a,int b,int c){return 0;}
Functions can have compile time arguments in the form of a template. Seefunction templates.
In contexts where a compile time value is required, functions can be used to compute those values. This is calledCompile Time Function Execution, orCTFE.
These contexts are:
enum eval(alias arg) = arg;int square(int i){return i * i;}void main(){import std.stdio;static j = square(3);// CTFE writeln(j);assert(square(4) == 16);// run timestaticassert(square(3) == 9);// CTFE writeln(eval!(square(5)));// CTFE}
The function must have aFunctionBody.
CTFE is subject to the following restrictions:
Pointers are permitted in CTFE, provided they are used safely:
The above restrictions apply only to expressions which are actually evaluated. For example:
staticint y = 0;int countTen(int x){if (x > 10) ++y;// access static variablereturn x;}staticassert(countTen(6) == 6);// OKstaticassert(countTen(12) == 12);// invalid, modifies y.
The__ctfe boolean pseudo-variable evaluates totrue during CTFE butfalse otherwise.
Non-recoverable errors (such asassert failures) are illegal.
All functions that execute in CTFE must also be executable at run time. The compile time evaluation of a function does the equivalent of running the function at run time. The semantics of a function cannot depend on compile time values of the function. For example:
int foo(string s){returnmixin(s);}constint x = foo("1");is illegal, because the runtime code forfoo cannot be generated.
No-GC functions are functions marked with the@nogc attribute. Those functions do not allocate memory on the GC heap. These operations are not allowed in No-GC functions:
@nogcvoid foo(){auto a = ['a'];// (1) error, allocates a.length = 1;// (2) error, array resizing allocates a = a ~ a;// (3) error, arrays concatenation allocates a ~= 'c';// (4) error, appending to arrays allocatesauto aa = ["x":1];// (5) error, allocates aa["abc"];// (6) error, indexing may allocate and throwsauto p =newint;// (7) error, operator new allocatesscopeauto p =new GenericClass();// (7) Ok bar();// (8) error, bar() may allocatedebug bar();// (8) Ok}void bar() { }
No-GC functions can only use a closure if it isscope - seeDelegates & Closures.
@nogcintdelegate() foo(){int n;// error, variable n cannot be allocated on heapreturn (){return n; }// since `n` escapes `foo()`, a closure is required}
@nogc affects the type of the function. A@nogc function is covariant with a non-@nogc function.
voidfunction() fp;voidfunction() @nogc gp;// pointer to @nogc functionvoid foo();@nogcvoid bar();void test(){ fp = &foo;// ok fp = &bar;// ok, it's covariant gp = &foo;// error, not contravariant gp = &bar;// ok}
Safe functions are marked with the@safe attribute.@safe can be inferred, seeFunction Attribute Inference.
Safe functions havesafe interfaces. An implementation must enforce this by restricting the function's body to operations that are known to be safe, except for calls to@trusted functions.
The following restrictions are enforced by the compiler in safe functions:
Functions nested inside safe functions default to being safe functions.
Safe functions are covariant with trusted or system functions.
External functions don't have a function body visible to the compiler:
@safeextern (C)void play();and so safety cannot be verified automatically.
Trusted functions are marked with the@trusted attribute.
Likesafe functions, trusted functions havesafe interfaces. Unlike safe functions, this is not enforced by restrictions on the function body. Instead, it is the responsibility of the programmer to ensure that the interface of a trusted function is safe.
Example:
immutable(int)* f(int* p) @trusted{version (none) p[2] = 13;// Invalid. p[2] is out of bounds. This line would exhibit undefined// behavior.version (none) p[1] = 13;// Invalid. In this program, p[1] happens to be in-bounds, so the// line would not exhibit undefined behavior, but a trusted function// is not allowed to rely on this.version (none)returncast(immutable) p;// Invalid. @safe code still has mutable access and could trigger// undefined behavior by overwriting the value later on.int* p2 =newint; *p2 = 42;returncast(immutable) p2;// Valid. After f returns, no mutable aliases of p2 can exist.}void main() @safe{int[2] a = [10, 20];int* mp = &a[0];immutable(int)* ip = f(mp);assert(a[1] == 20);// Guaranteed. f cannot access a[1].assert(ip !is mp);// Guaranteed. f cannot introduce unsafe aliasing.}
Trusted functions may call safe, trusted, or system functions.
Trusted functions are covariant with safe or system functions.
System functions are functions not marked with@safe or@trusted and are not nested inside@safe functions. System functions may be marked with the@system attribute. A function being system does not mean it actually is unsafe, it just means that its safety must be manually verified.
System functions arenot covariant with trusted or safe functions.
System functions can call safe and trusted functions.
When a function call's arguments,any context and accessible globals each havesafe values withsafe aliasing, that function has a safe interface when:
Functions that meet these requirements may be@safe or@trusted. Function that do not meet these requirements can only be@system.
Examples:
extern (C) @systemvoid free(void* ptr);becausefree(p) invalidatesp, making its value unsafe.free can only be@system.
extern (C) @system size_t strlen(char* s);extern (C) @systemvoid* memcpy(void* dst,void* src, size_t nbytes);because they iterate pointers based on unverified assumptions (strlen assumes thats is zero-terminated;memcpy assumes that the memory objects pointed to bydst andsrc are at leastnbytes big). Any function that traverses a C string passed as an argument can only be@system. Any function that trusts a separate parameter for array bounds can only be@system.
extern (C) @trustedvoid* malloc(size_t sz);It does not exhibit undefined behavior for any input. It returns either a valid pointer, which is safe, ornull which is also safe. It returns a pointer to a fresh allocation, so it cannot introduce any unsafe aliasing.
@safevoid memcpy(E)(E[] src, E[] dst){import std.math : min;foreach (i; 0 .. min(src.length, dst.length)) { dst[i] = src[i]; }}
A variable or field marked as@system does not hold a safe value, regardless of its type.
For abool, only 0 and 1 are safe values.
For all otherbasic data types, all possible bit patterns are safe.
A pointer is a safe value when it is one of:
Examples:
int* n =null;/* n is safe because dereferencing null must either crash or abort. */int* x =cast(int*) 0xDEADBEEF;/* x is (most likely) unsafe because it is not a valid pointer and cannot be dereferenced. */import core.stdc.stdlib: malloc, free;int* p1 =cast(int*) malloc(int.sizeof);/* p1 is safe because the pointer is valid and *p1 is safe regardless of its actual value. */free(p1);/* This makes p1 unsafe. */int** p2 = &p1;/* While it can be dereferenced, p2 is unsafe because p1 is unsafe. */p1 =null;/* This makes p1 and p2 safe. */
A dynamic array is safe when:
Examples:
int[] f() @system{bool b =true;/* b is initialized safe */ *(cast(ubyte*) &b) = 0xAA;/* b is now unsafe because it's not 0 or 1 */int[3] a;int[] d1 = a[0 .. 3];/* d1 is safe. */int[] d2 = a.ptr[0 .. 4];/* d2 is unsafe because it goes beyond a's bounds. */int*[] d3 = [cast(int*) 0xDEADBEEF];/* d3 is unsafe because the element is unsafe. */return d1;/* Up to here, d1 was safe, but its pointer becomes invalid when the function returns, so the returned dynamic array is unsafe. */}
A static array is safe when all its elements are safe. Regardless of the element type, a static array with length zero is always safe.
An associative array is safe when all its keys and elements are safe.
A struct/union instance is safe when:
Examples:
void fun(){struct S {int* p; } S s1 = S(newint);/* s1 is safe. */ S s2 = S(cast(int*) 0xDEADBEEF);/* s2 is unsafe, because s2.p is unsafe. */union U {int* p; size_t x; } U u = U(newint);/* Even though both u.p and u.x are safe, u is unsafe because of unsafe aliasing. */}
A class reference is safe when it isnull or:
A function pointer is safe when it isnull or it refers to a valid function that has the same or a covariant signature.
Adelegate is safe when:
When generating@safe code, a compliant implementation:
When one memory location is accessible with two different types, that aliasing is considered safe if:
All other cases of aliasing are considered unsafe.
Examples:
void f1(refubyte x,reffloat y) @safe { x = 0; y =float.init; }union U1 {ubyte x;float y; }// safe aliasingvoid test1(){ U1 u1; f1(u1.x, u1.y);// Ok}void f2(refint* x,refint y) @trusted { x =newint; y = 0xDEADBEEF; }union U2 {int* x;int y; }// unsafe aliasingvoid test2(){ U2 u2;version (none) f1(u2.x, u2.y);// not safe}
FunctionLiterals,Auto Functions,Auto Ref Functions,nested functions andfunction templates, since their function bodies are always present, infer the following attributes unless specifically overridden:
Attribute inference is not done for other functions, even if the function body is present.
The inference is done by determining if the function body follows the rules of the particular attribute.
Cyclic functions (i.e. functions that wind up directly or indirectly calling themselves) are inferred as being impure, throwing, and@system.
If a function attempts to test itself for those attributes, then the function is inferred as not having those attributes.
A free function can be called like a member function when both:
The object expression can be any type. This is called aUFCS function call.
void sun(T,int);void moon(T t){ t.sun(1);// If `T` does not have a member function `sun`,// `t.sun(1)` is interpreted as if it were written `sun(t, 1)`}
A more complex example:
stdin.byLine(KeepTerminator.yes) .map!(a => a.idup) .array .sort .copy(stdout.lockingTextWriter());
is the equivalent of:
copy(sort(array(map!(a => a.idup)(byLine(stdin, KeepTerminator.yes)))), lockingTextWriter(stdout));
UFCS works with@property functions:
@property prop(X thisObj);@property prop(X thisObj,int value);X obj;obj.prop;// if X does not have member prop, reinterpret as prop(obj);obj.prop = 1;// similarly, reinterpret as prop(obj, 1);
Functions declared in a local scope are not found when searching for a matching UFCS function. Neither are other local symbols, although local imports are searched:
module a;void foo(X);alias boo = foo;void main(){void bar(X);// bar declared in local scopeimport b : baz;// void baz(X); X obj; obj.foo();// OK, calls a.foo;//obj.bar(); // NG, UFCS does not see nested functions obj.baz();// OK, calls b.baz, because it is declared at the// top level scope of module bimport b : boo = baz; obj.boo();// OK, calls aliased b.baz instead of a.boo (== a.foo),// because the declared alias name 'boo' in local scope// overrides module scope name}
Member functions are not found when searching for a matching UFCS function.
class C{void mfoo(X);// member functionstaticvoid sbar(X);// static member functionimport b : ibaz = baz;// void baz(X);void test() { X obj;//obj.mfoo(); // NG, UFCS does not see member functions//obj.sbar(); // NG, UFCS does not see static member functions obj.ibaz();// OK, ibaz is an alias of baz which is declared at// the top level scope of module b }}
Otherwise, UFCS function lookup proceeds normally.
int front(int[] arr) {return arr[0]; }void main(){int[] a = [1,2,3];auto x = a.front();// call .front by UFCSauto front = x;// front is now a variableauto y = a.front();// Error, front is not a function}class C{int[] arr;int front() {return arr.front();// Error, C.front is not callable// using argument types (int[]) }})