If you spot a problem with this page, click here to create a Bugzilla issue.
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 usinga local clone.
Contents
- Grammar
- Contracts
- Function Return Values
- Functions Without Bodies
- Pure Functions
- Nothrow Functions
- Ref Functions
- Auto Functions
- Auto Ref Functions
- Inout Functions
- Optional Parentheses
- Property Functions
- Virtual Functions
- Function Inheritance and Overriding
- Inline Functions
- Function Overloading
- Overload Sets
- Function Parameters
- Function Default Arguments
- Return Ref Parameters
- Variadic Functions
- Local Variables
- Local Static Variables
- Nested Functions
- Delegates, Function Pointers, and Closures
- Anonymous Functions and Anonymous Delegates
- main() Function
- Function Templates
- Compile Time Function Execution (CTFE)
- String Mixins and Compile Time Function Execution
- Function Safety
- Safe Functions
- Trusted Functions
- System Functions
- Function Attribute Inference
- Uniform Function Call Syntax (UFCS)
Grammar
FuncDeclaration: StorageClassesopt BasicType FuncDeclarator FunctionBody AutoFuncDeclarationAutoFuncDeclaration: StorageClasses Identifier FuncDeclaratorSuffix FunctionBodyFuncDeclarator: BasicType2opt Identifier FuncDeclaratorSuffixFuncDeclaratorSuffix: Parameters MemberFunctionAttributesopt TemplateParameters Parameters MemberFunctionAttributesopt Constraintopt
Parameters: ( ParameterListopt )ParameterList: Parameter Parameter , ParameterList ...Parameter: InOutopt BasicType Declarator InOutopt BasicType Declarator ... InOutopt BasicType Declarator = AssignExpression InOutopt Type InOutopt Type ...InOut: InOutX InOut InOutXInOutX: auto TypeCtor final in lazy out ref return ref scopeFunctionAttributes: FunctionAttribute FunctionAttribute FunctionAttributesFunctionAttribute: nothrow pure PropertyMemberFunctionAttributes: MemberFunctionAttribute MemberFunctionAttribute MemberFunctionAttributesMemberFunctionAttribute: const immutable inout return shared FunctionAttribute
FunctionBody: BlockStatement FunctionContractsopt BodyStatement FunctionContractsFunctionContracts: InStatement OutStatementopt OutStatement InStatementoptInStatement: in BlockStatementOutStatement: out BlockStatement out ( Identifier ) BlockStatementBodyStatement: body BlockStatement
Contracts
The in and out blocks of a function declaration specifythe pre- and post-conditions of the function. They are used inContract Programming.The code inside these blocks shouldnot have any side-effects, including modifying function parametersand/or return values.
Function Return Values
Function return values are considered to be rvalues.This means they cannot be passed by reference to other functions.
Functions Without Bodies
Functions without bodies:
int foo();
that are not declared as abstract are expected to have their implementationselsewhere, and that implementation will be provided at the link step.This enables an implementation of a function to be completely hidden from the userof it, and the implementation may be in another language such as C, assembler, etc.
Pure Functions
Pure functions are functions that cannot access global/staticmutable state, except if their arguments contain pointers to such. Thisenables optimizations based on the fact that a pure function may at mostmutate state reachable through its parameters. To that end, a purefunction:
- does not read or write any global or static mutable state
- cannot call functions that are not pure
- can override an impure function, but cannot be overridden by an impure function
- is covariant with an impure function
- cannot perform I/O
This definition of mutable functions is more general than the onetraditionally employed by pure functional languages because it allows aD pure function to use state mutation, as long as all state is createdinternally or reachable through its arguments. In particular, a purefunction may allocate memory by means of e.g. new or malloc withoutthese being special cases. A pure function is allowed to loopindefinitely or terminate the program.
As a concession to practicality, a pure function can also:
- read and write the floating point exception flags
- read and write the floating point mode flags, as long as thoseflags are restored to their initial state upon function entry
- perform impure operations in statements that are in aConditionalStatementcontrolled by a DebugCondition.
A pure function can throw exceptions.
import std.stdio;int x;immutable int y;const int* pz;pure int foo(int i, char* p, const char* q, immutable int* s){ debug writeln("in foo()"); // ok, impure code allowed in debug statement x = i; // error, modifying global state i = x; // error, reading mutable global state i = y; // ok, reading immutable global state i = *pz; // error, reading const global state return i;}
An implementation may assume that a pure function that (a) acceptsonly parameters without mutable indirections, and (b) returns a resultwithout mutable indirections, will have the same effect for all invocationwith equivalent arguments, and is allowed to memoize the result of thefunction under the assumption that equivalent parameters always produceequivalent results. Such functions are termed strongly pure functionsin this document. Note that a strongly pure function may still have behaviorinconsistent with memoization by e.g. using casts or by changing behaviordepending on the address of its parameters. An implementation is currentlynot required to enforce validity of memoization in all cases.
A pure function that accepts only parameters without mutableindirections and returns a result that has mutable indirections is called apure factory function. An implementation may assume that all mutablememory returned by the call is not referenced by any other part of theprogram, i.e. it is newly allocated by the function. Conversely, the mutablereferences of the result may be assumed to not refer to any object thatexisted before the function call. 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, result); return result;}
Here, an implementation may assume (without having knowledge of the bodyof make) that all references in make's result refer to other Listobjects created by make, and that no other part of the program refers toany of these objects.
Any pure function that is not strongly pure cannot be assumed to bememoizable, and calls to it may not be elided even if it returns void(save for compiler optimizations that prove the function has no effect).Function calls may still be elided, or results be memoized, by means oftraditional inlining and optimization techniques available for allfunctions.
If a strongly pure function throws an exception or an error, theassumptions related to memoization and references do not carry to the thrownexception.
Pure destructors do not benefit of special elision.
Nothrow Functions
Nothrow functions can only throw exceptions derivedfrom class Error.
Nothrow functions are covariant with throwing ones.
Ref Functions
Ref functions allow functions to return by reference.This is analogous to ref function parameters.
ref int foo(){ auto p = new int; return *p;}...foo() = 3; // reference returns can be lvalues
Auto Functions
Auto functions have their return type inferred from anyReturnStatements in the function body.
An auto function is declared without a return type.If it does not already have a storage class, use theauto storage class.
If there are multiple ReturnStatements, the typesof them must be implicitly convertible to a common type.If there are no ReturnStatements, the return type is inferredto be void.
auto foo(int x) { return x + 3; } // inferred to be intauto bar(int x) { return x; return 2.5; } // inferred to be double
Auto Ref Functions
Auto ref functions infer their return type just asauto functions do.In addition, they become ref functionsif all return expressions are lvalues,and it would not be a reference to a local or a parameter.
auto ref f1(int x) { return x; } // value returnauto ref f2() { return 3; } // value returnauto ref f3(ref int x) { return x; } // ref returnauto ref f4(out int x) { return x; } // ref returnauto ref f5() { static int x; return x; } // ref return
The ref-ness of a function is determined from allReturnStatements in the function body:
auto ref f1(ref int x) { return 3; return x; } // ok, value returnauto ref f2(ref int x) { return x; return 3; } // ok, value returnauto ref f3(ref int x, ref double y){ return x; return y; // The return type is deduced to double, but cast(double)x is not an lvalue, // then become a value return.}
Auto ref function can have explicit return type.
auto ref int (ref int x) { return x; } // ok, ref returnauto ref int foo(double x) { return x; } // error, cannot convert double to int
Inout Functions
Functions that deal with mutable, const, or immutable types withequanimity often need to transmit their type to the return value:
int[] f1(int[] a, int x, int y) { return a[x .. y]; }const(int)[] f2(const(int)[] a, int x, int y) { return a[x .. y]; }immutable(int)[] f3(immutable(int)[] a, int x, int y) { return a[x .. y]; }
The code generated by these three functions is identical.To indicate that these can be one function, the inouttype constructor is employed:
inout(int)[] foo(inout(int)[] a, int x, int y) { return a[x .. y]; }
The inout forms a wildcard that stands in forany of mutable, const, immutable, inout, or inout const. When thefunction is called, the inout of the return type is changed to whateverthe mutable, const, immutable, inout, or inout const status of theargument type to the parameter inout was.
Inout types can be implicitly converted to const or inout const,but to nothing else. Other types cannot be implicitly converted to inout.Casting to or from inout is not allowed in @safe functions.
A set of arguments to a function with inout parameters is considereda match if any inout argument types match exactly, or:
- No argument types are composed of inout types.
- A mutable, const or immutable argument type can be matched against eachcorresponding parameter inout type.
If such a match occurs, the inout is considered the common qualifier ofthe matched qualifiers. If more than two parameters exist, the commonqualifier calculation is recursively applied.
mutable | const | immutable | inout | inout const | |
mutable (= m) | m | c | c | c | c |
const (= c) | c | c | c | c | c |
immutable (= i) | c | c | i | wc | wc |
inout (= w) | c | c | wc | w | wc |
inout const (= wc) | c | c | wc | wc | wc |
The inout in the return type is then rewritten to be the inout matchedqualifiers:
int[] ma;const(int)[] ca;immutable(int)[] ia;inout(int)[] foo(inout(int)[] a) { return a; }void test1(){ // inout matches to mutable, so inout(int)[] is // rewritten to int[] int[] x = foo(ma); // inout matches to const, so inout(int)[] is // rewritten to const(int)[] const(int)[] y = foo(ca); // inout matches to immutable, so inout(int)[] is // rewritten to immutable(int)[] immutable(int)[] z = foo(ia);}inout(const(int))[] bar(inout(int)[] a) { return a; }void test2(){ // inout matches to mutable, so inout(const(int))[] is // rewritten to const(int)[] const(int)[] x = bar(ma); // inout matches to const, so inout(const(int))[] is // rewritten to const(int)[] const(int)[] y = bar(ca); // inout matches to immutable, so inout(int)[] is // rewritten to immutable(int)[] immutable(int)[] z = bar(ia);}
Note: Shared types are not overlooked. Shared types cannotbe matched with inout.
Optional Parentheses
If a function call passes no explicit argument, i.e. it would syntactically use (), then these parenthesesmay be omitted, similar to a getter invocation of aproperty function.
void foo() {} // no argumentsvoid fun(int x = 10) { }void bar(int[] arr) {} // for UFCSvoid main(){ foo(); // OK foo; // also OK fun; // OK int[] arr; arr.bar(); // OK arr.bar; // also OK}
Optional parentheses are not applied to delegates or function pointers.
void main(){ int function() fp; assert(fp == 6); // Error, incompatible types int function() and int assert(*fp == 6); // Error, incompatible types int() and int int delegate() dg; assert(dg == 6); // Error, incompatible types int delegate() and int}
If a function returns a delegate or function pointer, the parantheses are required if thereturned value is to be called.
struct S { int function() callfp() { return &numfp; } int delegate() calldg() { return &numdg; } int numdg() { return 6; }}int numfp() { return 6; }void main(){ S s; int function() fp; fp = s.callfp; assert(fp() == 6); fp = s.callfp(); assert(fp() == 6); int x = s.callfp()(); assert(x == 6); int delegate() dg; dg = s.calldg; assert(dg() == 6); dg = s.calldg(); assert(dg() == 6); int y = s.calldg()(); assert(y == 6);}
Property Functions
Properties are functions that can be syntactically treatedas 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 argumentbeing the value it is set to.
Simple getter and setter properties can be written using UFCS.These can be enhanced with the additon of the @property attribute to the function, whichadds the following behaviors:
- @property functions cannot be overloaded with non-@property functions with the same name.
- @property functions can only have zero, one or two parameters.
- @property functions cannot have variadic parameters.
- For the expression typeof(exp) where exp is an @property function,the type is the return type of the function, rather than the type of the function.
- For the expression __traits(compiles, exp) where exp is an @property function,a further check is made to see if the function can be called.
- @property are mangled differently, meaning that @property must be consistentlyused across different compilation units.
- The ObjectiveC interface recognizes @property setter functions as special and modifiesthem accordingly.
A simple property would be:
struct Foo{ @property int data() { return m_data; } // read property @property int data(int value) { return m_data = value; } // write property private: 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 usingthe 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 .mangleofmay 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
Virtual functions are functions that are called indirectly through afunction pointer table, called a vtbl[], rather than directly. Allpublic and protected member functions which are non-static andare not templatized are virtual unless the compiler can determine thatthey will never be overridden (e.g. they are marked with final anddo not override any functions in a base class), in which case, it willmake them non-virtual. This results in fewer bugs caused by notdeclaring a function virtual and then overriding it anyway.
Member functions which are private or package are nevervirtual, and hence cannot be overridden.
Functions with non-D linkage cannot be virtual and hence cannot beoverridden.
Member template functions cannot be virtual and hence cannot beoverridden.
Functions marked as final may not be overridden in aderived class, unless they are also private.For example:
class A{ int def() { ... } final int foo() { ... } final private int bar() { ... } private int abc() { ... }}class B : A{ override int def() { ... } // ok, overrides A.def override int foo() { ... } // error, A.foo is final int bar() { ... } // ok, A.bar is final private, but not virtual int abc() { ... } // ok, A.abc is not virtual, B.abc is virtual}void test(A a){ a.def(); // calls B.def a.foo(); // calls A.foo a.bar(); // calls A.bar a.abc(); // calls A.abc}void func(){ B b = new B(); test(b);}
Covariant return typesare supported, which means that theoverriding function in a derived class can return a typethat is derived from the type returned by the overridden function:
class A { }class B : A { }class Foo{ A test() { return null; }}class Bar : Foo{ // overrides and is covariant with Foo.test() override B test() { return null; }}
Virtual functions all have a hidden parameter called thethis reference, which refers to the class object for whichthe function is called.
To avoid dynamic binding on member function call, insertbase class name before the member function name. For example:
class B{ int foo() { return 1; }}class C : B{ override int 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{ override int foo() { return 3; }}void main(){ auto d = new D(); assert(d.foo() == 3); // calls D.foo assert(d.B.foo() == 1); // calls B.foo assert(d.C.foo() == 2); // calls C.foo d.test();}
Function Inheritance and Overriding
A function in a derived class with the same name and parametertypes as a function in a base class overrides that function:
class A{ int foo(int x) { ... }}class B : A{ override int foo(int x) { ... }}void test(){ B b = new B(); bar(b);}void bar(A a){ a.foo(1); // calls B.foo(int)}
However, when doing overload resolution, the functions in the baseclass are not considered:
class A{ int foo(int x) { ... } int foo(long y) { ... }}class B : A{ override int foo(long x) { ... }}void test(){ B b = new B(); b.foo(1); // calls B.foo(long), since A.foo(int) not considered A a = b; a.foo(1); // issues runtime error (instead of calling A.foo(int))}
To consider the base class's functions in the overload resolutionprocess, use an AliasDeclaration:
class A{ int foo(int x) { ... } int foo(long y) { ... }}class B : A{ alias foo = A.foo; override int foo(long x) { ... }}void test(){ B b = new B(); bar(b);}void bar(A a){ a.foo(1); // calls A.foo(int) B b = new B(); b.foo(1); // calls A.foo(int)}
If such an AliasDeclaration is not used, the derivedclass's functions completely override all the functions of thesame name in the base class, even if the types of the parametersin the base class functions are different. If, throughimplicit conversions to the base class, those other functions doget called, a compile-time error will be given:
class A{ void set(long i) { } void set(int i) { }}class B : A{ void set(long i) { }}void foo(A a){ int i; a.set(3); // error, use of A.set(int) is hidden by B // use 'alias set = A.set;' to introduce base class overload set. assert(i == 1);}void main(){ foo(new B);}
If an error occurs during the compilation of your program,the use of overloads and overrides needs to be reexamined in therelevant classes.
The compiler will not give an error if the hidden functionis disjoint, as far as overloading is concerned, from all theother virtual functions is the inheritance hierarchy.
A function parameter's default value is not inherited:
class A{ void foo(int x = 5) { ... }}class B : A{ void foo(int x = 7) { ... }}class C : B{ void foo(int x) { ... }}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}
If a derived class overrides a base class member function with diferrentFunctionAttributes, the missing attributes will beautomatically compensated by the compiler.
class B{ void foo() pure nothrow @safe {}}class D : B{ override void foo() {}}void main(){ auto d = new D(); pragma(msg, typeof(&d.foo)); // prints "void delegate() pure nothrow @safe" in compile time}
It's not allowed to mark an overridden method with the attributes@disable ordeprecated.To stop the compilation or to output the deprecation message, the compilermust be able to determine the target of the call, which can't be guaranteedwhen it is virtual.
class B{ void foo() {}}class D : B{ @disable override void foo() {}}void main(){ B b = new D; b.foo(); // compiles and calls the most derived even if disabled.}
Inline Functions
The compiler makes the decision whether to inline a function or not.This decision may be controlled by pragma(inline),assuming that the compiler implements it, which is not mandatory.
Note that any FunctionLiteral should be inlinedwhen used in its declaration scope.
Function Overloading
Functions are overloaded based on how well the argumentsto a function can match up with the parameters.The function with the best match is selected.The levels of matching are:
- no match
- match with implicit conversions
- match with conversion to const
- exact match
Each argument (including any this pointer) iscompared against the function's corresponding parameter, todetermine the match level for that argument. The match levelfor a function is the worst match level of each of itsarguments.
Literals do not match ref or out parameters.
If two or more functions have the same match level,then partial orderingis used to try 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 functions f()and g() by taking the parameter types of f(),constructing a list of arguments by taking the default valuesof those types, and attempting to match them against g().If it succeeds, then g() is at least as specializedas f().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 conversion rules. * 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 lessspecialized than a function without.
Functions defined with non-D linkage cannot be overloaded.This is because the name mangling might not take the parameter typesinto account.
Overload Sets
Functions declared at the same scope overload against eachother, and are called an Overload Set.A typical example of an overload set are functions definedat module level:
module A;void foo() { }void foo(long i) { }
A.foo() and A.foo(long) form an overload set.A different module can also define 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, the A.foo overload set and the B.foooverload set, are found. An instance of foo is selectedbased on it matching in exactly one overload set:
import A;import B;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); // error, matches A.foo(long) and B.foo(int) A.foo(1); // calls A.foo(long)}
Even though B.foo(int) is a better match than A.foo(long) for foo(1),it is an error because the two matches are indifferent 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)}
Function Parameters
Parameter storage classes are in, out,ref, lazy, const, immutable, shared,inout orscope.For example:
int foo(in int x, out int y, ref int z, int q);
x is in, y is out, z is ref, and q is none.
- The function declaration makes it clear what the inputs andoutputs to the function are.
- It eliminates the need for IDL (interface description language) as a separate language.
- It provides more information to the compiler, enabling moreerror checking andpossibly better code generation.
Storage Class | Description |
---|---|
none | parameter becomes a mutable copy of its argument |
in | equivalent to const scope |
out | parameter is initialized upon function entry with the default valuefor its type |
ref | parameter is passed by reference |
scope | references in the parametercannot be escaped (e.g. assigned to a global variable).Ignored for parameters with no references |
lazy | argument is evaluated by the called function and not by the caller |
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 |
void foo(out int x){ // x is set to int.init, // which is 0, at start of foo()}int a = 3;foo(a);// a is now 0void abc(out int x){ x = 2;}int y = 3;abc(y);// y is now 2void def(ref int x){ x += 1;}int z = 3;def(z);// z is now 4
For dynamic array and object parameters, which are passedby reference, in/out/refapply only to the reference and not the contents.
lazy arguments are evaluated not when the function is called,but when the parameter is evaluated within the function. Hence,a lazy argument can be executed 0 or more times. A lazy parametercannot be an lvalue.
void dotimes(int n, lazy void exp){ while (n--) exp();}void test(){ int x; dotimes(3, writeln(x++));}
prints to the console:
012
A lazy parameter of type void can accept an argumentof any type.
Function Default Arguments
Function parameter declarations can have default values:
void foo(int x, int y = 3){ ...}...foo(4); // same as foo(4, 3);
Default parameters are evaluated in the context of thefunction declaration.If the default value for a parameter is given, all followingparameters must also have default values.
Return Ref Parameters
Note: The return attribute is currently only enforced by dmdwhen the -dip25 switch is passed.
Return ref parameters are used withref functions to ensure that thereturned reference will not outlive the matching argument's lifetime.
ref int identity(return ref int x) { return x; // pass-through function that does nothing}ref int fun() { int x; return identity(x); // Error: escaping reference to local variable x}ref int gun(return ref int x) { return identity(x); // OK}
Ref methods marked with the return attribute ensure the returnedreference will not outlive the respective aggregate instance.
struct S{ private int x; ref int get() return { return x; }}ref int escape(){ S s; return s.get(); // Error: escaping reference to local variable s}
Template functions and lambdas can deduce the return attribute.
inout ref parameters imply the return attribute.
Variadic Functions
Functions taking a variable number of arguments are calledvariadic functions. A variadic function can take one ofthree forms:
- C-style variadic functions
- Variadic functions with type info
- Typesafe variadic functions
C-style Variadic Functions
A C-style variadic function is declared as takinga parameter of ... after the required function parameters.It has non-D linkage, such as extern (C):
extern (C) void foo(int x, int y, ...);foo(3, 4); // okfoo(3, 4, 6.8); // ok, one variadic argumentfoo(2); // error, y is a required argument
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 forvariadic functions, and is most useful for calling C libraryfunctions like printf.
C-style variadic functions cannot be marked as @safe.
Access to variadic arguments is done using the standard librarymodule core.stdc.stdarg.
import core.stdc.stdarg;void test(){ foo(3, 4, 5); // first variadic argument is 5}void foo(int x, int y, ...){ va_list args; va_start(args, y); // y is the last named parameter int z; va_arg(args, z); // z is set to 5}
D-style Variadic Functions
Variadic functions with argument and type info are declared as takinga parameter of ... after the required function parameters.It has D linkage, and need not have any non-variadic parametersdeclared:
int abc(char c, ...); // one required parameter: cint def(...); // ok
To access them, the following import is required:
import core.vararg;
These variadic functions have a special local variable declared forthem,_argptr, which is a core.varargreference to the first of the variadicarguments. To access the arguments, _argptr must be usedin conjuction with va_arg:
import core.vararg;void test(){ foo(3, 4, 5); // first variadic argument is 5}void foo(int x, int y, ...){ int z; z = va_arg!int(_argptr); // z is set to 5}
An additional hidden argumentwith the name _arguments and type TypeInfo[]is passed to the function._arguments gives the number of arguments and the typeof each, enabling type safety to be checked at run time.
import std.stdio;import core.vararg;class Foo { int x = 3; }class Bar { long y = 4; }void 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); } else if (_arguments[i] == typeid(long)) { long j = va_arg!(long)(_argptr); writefln("\t%d", j); } else if (_arguments[i] == typeid(double)) { double d = va_arg!(double)(_argptr); writefln("\t%g", d); } else if (_arguments[i] == typeid(Foo)) { Foo f = va_arg!(Foo)(_argptr); writefln("\t%s", f); } else if (_arguments[i] == typeid(Bar)) { Bar b = va_arg!(Bar)(_argptr); writefln("\t%s", b); } else assert(0); }}void main(){ Foo f = new Foo(); Bar b = new Bar(); writefln("%s", f); printargs(1, 2, 3L, 4.5, f, b);}
which prints:
0x00870FE05 argumentsint 2long 3double 4.5Foo 0x00870FE0Bar 0x00870FD0
D-style variadic functions cannot be marked as @safe.
Typesafe Variadic Functions
Typesafe variadic functions are used when the variable argumentportion of the arguments are used to construct an array orclass object.
For arrays:
int main(){ return sum(1, 2, 3) + sum(); // returns 6+0}int func(){ int[3] ii = [4, 5, 6]; return sum(ii); // returns 15}int sum(int[] ar ...){ int s; foreach (int x; ar) s += x; return s;}
For static arrays:
int test(){ return sum(2, 3); // error, need 3 values for array return sum(1, 2, 3); // returns 6}int func(){ int[3] ii = [4, 5, 6]; int[] jj = ii; return sum(ii); // returns 15 return sum(jj); // error, type mismatch}int sum(int[3] ar ...){ int s; foreach (int x; ar) s += x; return s;}
For class objects:
class Foo{ int x; string s; this(int x, string s) { this.x = x; this.s = s; }}void test(int x, Foo f ...);...Foo g = new Foo(3, "abc");test(1, g); // ok, since g is an instance of Footest(1, 4, "def"); // oktest(1, 5); // error, no matching constructor for Foo
An implementation may construct the object or array instanceon the stack. Therefore, it is an error to refer to thatinstance after the variadic function has returned:
Foo test(Foo f ...){ return f; // error, f instance contents invalid after return}int[] test(int[] a ...){ return a; // error, array contents invalid after return return a[0..1]; // error, array contents invalid after return return a.dup; // ok, since copy is made}
For other types, the argument is built with itself, as in:
int test(int i ...){ return i;}...test(3); // returns 3test(3, 4); // error, too many argumentsint[] x;test(x); // error, type mismatch
Lazy Variadic Functions
If the variadic parameter is an array of delegateswith no parameters:
void foo(int delegate()[] dgs ...);
Then each of the arguments whose type does not match thatof the delegate is converted to a delegate.
int delegate() dg;foo(1, 3+x, dg, cast(int delegate())null);
is the same as:
foo( { return 1; }, { return 3+x; }, dg, null );
Local Variables
It is an error to use a local variable without first assigning it avalue. The implementation may not always be able to detect thesecases. Other language compilers sometimes issue a warning for this,but since it is always a bug, it should be an error.
It is an error to declare a local variable that hides another localvariable in the same function:
void func(int x){ int x; // error, hides previous definition of x double y; ... { char y; // error, hides previous definition of y int z; } { wchar z; // legal, previous z is out of scope }}
While this might look unreasonable, in practice wheneverthis is done it either is abug or at least looks like a bug.
It is an error to return the address of or a reference to alocal variable.
It is an error to have a local variable and a label with the samename.
Local Static Variables
Local variables in functions can be declared as staticor __gshared in which case they are statically allocatedrather than being allocated on the stack.As such, their value persists beyond the exit of the function.
void foo(){ static int n; if (++n == 100) writeln("called 100 times");}
The initializer for a static variable must be evaluatable atcompile time, and they are initialized upon the start of the thread(or the start of the program for __gshared).There are no static constructors or static destructorsfor static local variables.
Although static variable name visibility follows the usual scopingrules, the names of them must be unique within a particular function.
void main(){ { static int x; } { static int x; } // error { int i; } { int i; } // ok}
Nested Functions
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 scope void 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); } // ok return foo(a);}void test(){ int i = bar(3); // ok int j = bar.foo(3); // error, bar.foo not visible}
Nested functions have access to the variables and other symbolsdefined 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 5 return b + c; // 12 is returned } c = 4; int i = foo(a); // i is set to 12 return 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 ofany lexically enclosing function, but can access static variables.This is analogous to how static member functions behave.
int bar(int a){ int c; static int d; static int 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 functionscope are processed in order. This means that two nested functionscannot mutually call each other:
void test(){ void foo() { bar(); } // error, bar not defined void bar() { foo(); } // ok}
There are several workarounds for this limitation:
- Declare the functions to be static members of a nested struct:
void test(){ static struct S { static void foo() { bar(); } // ok static void 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}
mixin template T(){ void foo() { bar(); } // ok void bar() { foo(); } // ok}void main(){ mixin T!();}
void test(){ void delegate() fp; void foo() { fp(); } void bar() { foo(); } fp = &bar;}
Nested functions cannot be overloaded.
Delegates, Function Pointers, and Closures
A function pointer can point to a static nested function:
int function() fp;void test(){ static int a = 7; static int foo() { return a + 3; } fp = &foo;}void bar(){ test(); int i = fp(); // i is set to 10}
Note: Two functions with identical bodies, or two functionsthat compile to identical assembly code, are not guaranteed to havedistinct function pointer values. The compiler is free to mergefunctions bodies into one if they compile to identical code.
int abc(int x) { return x + 1; }int def(int y) { return y + 1; }int delegate(int) fp1 = &abc;int delegate(int) fp2 = &def;// Do not rely on fp1 and fp2 being different values; the compiler may merge// them.
A delegate can be set to a non-static nested function:
int delegate() dg;void test(){ int a = 7; int foo() { return a + 3; } dg = &foo; int i = dg(); // i is set to 10}
The stack variables referenced by a nested function arestill valid even after the function exits (this is differentfrom D 1.0). This is called a closure.Returning addresses of stack variables, however, is nota closure and is an error.
int* bar(){ int b; test(); int i = dg(); // ok, test.a is in a closure and still exists return &b; // error, bar.b not valid after bar() exits}
Delegates to non-static nested functions contain two pieces ofdata: the pointer to the stack frame of the lexically enclosingfunction (called the frame pointer) and the address of thefunction. This is analogous to struct/class non-static memberfunction delegates consisting of a this pointer andthe address of the member function.Both forms of delegates are interchangeable, and are actuallythe same type:
struct Foo{ int a = 7; int bar() { return a; }}int foo(int delegate() dg){ return dg() + 1;}void test(){ int x = 27; int abc() { return x; } Foo f; int i; i = foo(&abc); // i is set to 28 i = foo(&f.bar); // i is set to 8}
This combining of the environment and the function is calleda dynamic closure.
The .ptr property of a delegate will return theframe pointer value as a void*.
The .funcptr property of a delegate will return thefunction pointer value as a function type.
Future directions: Function pointers and delegates may mergeinto a common syntax and be interchangeable with each other.
Anonymous Functions and Anonymous Delegates
See FunctionLiterals.
main() Function
For console programs, main() serves as the entry point.It gets called after all the module initializers are run, andafter any unittests are run.After it returns, all the module destructors are run.main() must be declared using one of the following forms:
void main() { ... }void main(string[] args) { ... }int main() { ... }int main(string[] args) { ... }
Function Templates
Template functions are useful for avoiding code duplication -instead of writing several copies of a function, each with adifferent parameter type, a single function template can be sufficient.For example:
// Only one copy of func needs to be writtenvoid func(T)(T x){ writeln(x);}void main(){ func!(int)(1); // pass an int func(1); // pass an int, inferring T = int func("x"); // pass a string func(1.0); // pass a float struct S {} S s; func(s); // pass a struct}
func takes a template parameter T and a runtimeparameter, x. T is a placeholder identifier that can acceptany type. In this case T can be inferred from the runtime argumenttype.
Note: Using the name T is just a convention. The nameTypeOfX could have been used instead.
For more information, seefunction templates.
Compile Time Function Execution (CTFE)
Functions which are both portable and free of side-effects can beexecuted at compile time. This is useful when constant foldingalgorithms need to include recursion and looping. Compile time functionexecution is subject to the following restrictions:
- The function source code must be available to the compiler. Functionswhich exist in the source code only as extern declarationscannot be executed at compile time.
- Executed expressions may not reference any global or localstatic variables.
- asm statements are not permitted
- Non-portable casts (eg, from int[] to float[]), includingcasts which depend on endianness, are not permitted.Casts between signed and unsigned types are permitted
- Reinterpretation of overlapped fields in a Union.
Pointers are permitted in CTFE, provided they are used safely:
- C-style semantics on pointer arithmetic are strictly enforced.Pointer arithmetic is permitted only on pointers which point to staticor dynamic array elements. Such pointers must point to an element ofthe array, or to the first element past the array.Pointer arithmetic is completely forbidden on pointers which are null,or which point to a non-array.
- The memory location of different memory blocks is not defined.Ordered comparison (<, <=, >, >=) between two pointers is permittedwhen both pointers point to the same array, or when at least onepointer is null.
- Pointer comparisons between independent memory blocks will generatea compile-time error, unless two such comparisons are combinedusing && or || to yield a result which is independent of theordering of memory blocks. Each comparison must consist of two pointerexpressions compared with <, <=, >,or >=, and may optionally benegated with !.
For example, the expression (p1 > q1 && p2 <= q2)is permitted when p1, p2 are expressions yielding pointersto memory block P, and q1, q2 are expressions yieldingpointers to memory block Q, even when P and Q areunrelated memory blocks.It returns true if [p1..p2] lies inside [q1..q2], and false otherwise.Similarly, the expression (p1 < q1 || p2 > q2) is true if[p1..p2] lies outside [q1..q2], and false otherwise.
- Equality comparisons (==, !=, is, !is) arepermitted between all pointers, without restriction.
- Any pointer may be cast to void * and from void * back toits original type. Casting between pointer and non-pointer types isprohibited.
Note that the above restrictions apply only to expressions which areactually executed. For example:
static int y = 0;int countTen(int x){ if (x > 10) ++y; return x;}static assert(countTen(6) == 6); // OKstatic assert(countTen(12) == 12); // invalid, modifies y.
The __ctfe boolean pseudo-variable, which evaluates to trueat compile time, but false at run time, can be used to providean alternative execution path to avoid operations which are forbiddenat compile time. Every usage of __ctfe is evaluated beforecode generation and therefore has no run-time cost, even if no optimizeris used.
In order to be executed at compile time, the functionmust appear in a context where it must be so executed, forexample:
- initialization of a static variable
- dimension of a static array
- argument for a template value parameter
template eval( A... ){ const typeof(A[0]) eval = A[0];}int square(int i){ return i * i;}void foo(){ static j = square(3); // compile time writeln(j); writeln(square(4)); // run time writeln(eval!(square(5))); // compile time}
Executing functions at compile time can take considerablylonger than executing it at run time.If the function goes into an infinite loop, it will hang atcompile time (rather than hanging at run time).
Non-recoverable errors (such as assert failures) do notthrow exceptions; instead, they end interpretation immediately.
Functions executed at compile time can give different resultsfrom run time in the following scenarios:
- floating point computations may be done at a higherprecision than run time
- dependency on implementation defined order of evaluation
- use of uninitialized variables
These are the same kinds of scenarios where differentoptimization settings affect the results.
String Mixins and Compile Time Function Execution
Any functions that execute at compile time must alsobe executable at run time. The compile time evaluation ofa function does the equivalent of running the function atrun time. This means that the semantics of a function cannotdepend on compile time values of the function. For example:
int foo(char[] s){ return mixin(s);}const int x = foo("1");
is illegal, because the runtime code for foo() cannot begenerated. A function template would be the appropriatemethod to implement this sort of thing.
Function Safety
Safe functions are functions that are statically checkedto exhibit no possibility ofundefined behavior.Undefined behavior is often used as a vector for maliciousattacks.
Safe Functions
Safe functions are marked with the @safe attribute.
The following operations are not allowed in safefunctions:
- No casting from a pointer type to any type other than void*.
- No casting from any non-pointer type to a pointer type.
- No modification of pointer values.
- Cannot access unions that have pointers or references overlappingwith other types.
- Calling any system functions.
- No catching of exceptions that are not derived from class Exception.
- No inline assembler.
- No explicit casting of mutable objects to immutable.
- No explicit casting of immutable objects to mutable.
- No explicit casting of thread local objects to shared.
- No explicit casting of shared objects to thread local.
- No taking the address of a local variable or function parameter.
- Cannot access __gshared variables.
Functions nested inside safe functions default to beingsafe functions.
Safe functions are covariant with trusted or system functions.
Note: The verifiable safety of functions may be compromised bybugs in the compiler and specification. Please report all such errorsso they can be corrected.
Trusted Functions
Trusted functions are marked with the @trusted attribute.
Trusted functions are guaranteed by the programmer to not exhibitany undefined behavior if called by a safe function.Generally, trusted functions should be kept small so that they areeasier to manually verify.
Trusted functions may call safe, trusted, or system functions.
Trusted functions are covariant with safe or system functions.
System Functions
System functions are functions not marked with @safe or@trustedand 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 justmeans that the compiler is unable to verify that it cannot exhibitundefined behavior.
System functions are not covariant with trusted or safe functions.
Function Attribute Inference
FunctionLiterals andfunction templates, since their function bodiesare always present, infer thepure,nothrow,@safe, and@nogc attributes unlessspecifically overridden.
Attribute inference is not done for other functions, even if the functionbody is present.
The inference is done by determining if the function body follows therules of the particular attribute.
Cyclic functions (i.e. functions that wind up directly or indirectlycalling themselves) are inferred as being impure, throwing, and @system.
If a function attempts to test itself for those attributes, thenthe function is inferred as not having those attributes.
Uniform Function Call Syntax (UFCS)
A free function can be called with a syntax that looks as if the functionwere a member function of its first parameter type.
void func(X thisObj);X obj;obj.func();// If 'obj' does not have regular member 'func',// it's automatically rewritten to 'func(obj)'
This provides a way to add functions to a class externally as if they werepublic final member functions, which enables function chaining and component programming.
stdin.byLine(KeepTerminator.yes) .map!(a => a.idup) .array .sort .copy(stdout.lockingTextWriter());
It also works with @property functions:
@property prop(X thisObj);@property prop(X thisObj, int value);X obj;obj.prop; // Rewrites to: prop(obj);obj.prop = 1; // Rewrites to: prop(obj, 1);
Syntactically parenthesis-less check for @propertyfunctions is done at the same time as UFCS rewrite.
When UFCS rewrite is necessary, compiler searches the nameon accessible module level scope, in order from the innermost scope.
module a;void foo(X);alias boo = foo;void main(){ void bar(X); import 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 b import 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}class C{ void mfoo(X); static void sbar(X); import 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 declared at // the top level scope of module b }}
The reason why local symbols are not considered by UFCS, isto avoid unexpected name conflicts. See below problematic examples.
int front(int[] arr) { return arr[0]; }void main(){ int[] a = [1,2,3]; auto x = a.front(); // call .front by UFCS auto front = x; // front is now a variable auto 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[]) }}
Type Qualifiers
Operator Overloading
Copyright © 1999-2018 by the D Language Foundation | Page generated byDdoc on (no date time)