Movatterモバイル変換


[0]ホーム

URL:


D Logo
Menu
Search

Language Reference

table of contents

Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page.Requires a signed-in GitHub account. This works well for small changes.If you'd like to make larger changes you may want to consider usinga local clone.

Statements

Contents
  1. Scope Statements
  2. Scope Block Statements
  3. Labeled Statements
  4. Block Statement
  5. Expression Statement
  6. Declaration Statement
  7. If Statement
    1. Boolean Conversion
    2. Condition Variables
  8. While Statement
  9. Do Statement
  10. For Statement
  11. Foreach Statement
    1. Foreach over Arrays
    2. Foreach over Arrays of Characters
    3. Foreach over Associative Arrays
    4. Foreach over Structs and Classes withopApply
    5. Foreach over Structs and Classes with Ranges
    6. Foreach over Delegates
    7. Foreach over Sequences
    8. Foreach Ref Parameters
    9. Foreach Restrictions
    10. Foreach Range Statement
  12. Switch Statement
    1. Case Range Statement
    2. No Implicit Fall-Through
    3. String Switch
  13. Final Switch Statement
  14. Continue Statement
  15. Break Statement
  16. Return Statement
  17. Goto Statement
  18. With Statement
  19. Synchronized Statement
  20. Try Statement
  21. Scope Guard Statement
    1. Catching C++ Class Objects
  22. Asm Statement
  23. Pragma Statement
  24. Mixin Statement

The order of execution within a function is controlled byStatements. A function's body consists of a sequence of zero or moreStatements. Execution occurs in lexical order, though certain statements may have deferred effects. AStatement has no value; it is executed for its effects.

Statement:EmptyStatementNonEmptyStatementScopeBlockStatement
EmptyStatement:;
NoScopeNonEmptyStatement:NonEmptyStatementBlockStatement
NoScopeStatement:EmptyStatementNonEmptyStatementBlockStatement
NonEmptyOrScopeBlockStatement:NonEmptyStatementScopeBlockStatement
NonEmptyStatement:NonEmptyStatementNoCaseNoDefaultCaseStatementCaseRangeStatementDefaultStatement
NonEmptyStatementNoCaseNoDefault:LabeledStatementExpressionStatementDeclarationStatementIfStatementWhileStatementDoStatementForStatementForeachStatementSwitchStatementFinalSwitchStatementContinueStatementBreakStatementReturnStatementGotoStatementWithStatementSynchronizedStatementTryStatementScopeGuardStatementAsmStatementMixinStatementForeachRangeStatementPragmaStatementConditionalStatementStaticForeachStatementImportDeclaration

Any ambiguities in the grammar betweenStatements andDeclarations are resolved by the declarations taking precedence. Wrapping such a statement in parentheses will disambiguate it in favor of being aStatement.

Scope Statements

ScopeStatement:NonEmptyStatementBlockStatement

A new scope for local symbols is introduced for theNonEmptyStatement orBlockStatement.

Even though a new scope is introduced, local symbol declarations cannot shadow (hide) other local symbol declarations in the same function.

void func1(int x){int x;// illegal, x shadows parameter xint y;    {int y; }// illegal, y shadows enclosing scope's yvoiddelegate() dg;    dg = {int y; };// ok, this y is not in the same functionstruct S    {int y;// ok, this y is a member, not a local    }    {int z; }    {int z; }// ok, this z is not shadowing the other z    {int t; }    { t++;   }// illegal, t is undefined}
Best Practices: Local declarations within a function should all have unique names, even if they are in non-overlapping scopes.

Scope Block Statements

ScopeBlockStatement:BlockStatement

A scope block statement introduces a new scope for theBlockStatement.

Labeled Statements

Statements can be labeled. A label is an identifier that precedes a statement.

LabeledStatement:Identifier:Identifier:Statement

Any statement can be labeled, including empty statements, and so can serve as the target of agoto statement. Labeled statements can also serve as the target of abreak orcontinue statement.

A label can appear without a following statement at the end of a block.

Labels are in a name space independent of declarations, variables, types, etc. Even so, labels cannot have the same name as local declarations. The label name space is the body of the function they appear in. Label name spaces do not nest, i.e. a label inside a block statement is accessible from outside that block.

Labels in one function cannot be referenced from another function.

Block Statement

BlockStatement:{ }{StatementList}
StatementList:StatementStatementStatementList

A block statement is a sequence of statements enclosed by{ }. The statements are executed in lexical order, until the end of the block is reached or a statement transfers control elsewhere.

Expression Statement

ExpressionStatement:Expression;

The expression is evaluated.

Expressions that have no effect, like(x + x), are illegal as expression statements unless they are cast tovoid.

int x;x++;// okx;// illegal1+1;// illegalcast(void)(x + x);// ok

Declaration Statement

Declaration statements define variables, and declare types, templates, functions, imports, conditionals, static foreaches, and static asserts.

DeclarationStatement:StorageClassesoptDeclaration

Some declaration statements:

int a;// declare a as type int and initialize it to 0struct S { }// declare struct salias myint =int;

If Statement

If statements provide simple conditional execution of statements.

IfStatement:if (IfCondition)ThenStatementif (IfCondition)ThenStatementelseElseStatement
IfCondition:ExpressionIfConditionStorageClassesIdentifier=ExpressionIfConditionStorageClassesoptBasicTypeDeclarator=Expression
IfConditionStorageClasses:IfConditionStorageClassIfConditionStorageClassIfConditionStorageClasses
IfConditionStorageClass:scopeautorefTypeCtor
ThenStatement:ScopeStatement
ElseStatement:ScopeStatement

If there is a declaredIdentifier variable,it is evaluated. Otherwise,Expression is evaluated. The result is converted to a boolean.

If the boolean istrue, theThenStatement is transferred to, otherwise theElseStatement is transferred to.

TheElseStatement is associated with the innermostif statement which does not already have an associatedElseStatement.

Boolean Conversion

The following type instances are supported in a condition:

If none of these are valid, it is an error to use the value in a condition.

Note: A dynamic arraya can be used in a condition, which istrue whena !is []. However, using an array as a boolean condition should be avoided as it can be bug-prone. This may be disallowed in a future edition.

Condition Variables

When anIdentifier form ofIfCondition is used, a variable is declared with that name and initialized to the value of theExpression.

The scope of the variable is theThenStatement only.

import std.regex;if (auto m = matchFirst("abcdef","b(c)d")){    writefln("[%s]", m.pre);// prints [a]    writefln("[%s]", m.post);// prints [ef]    writefln("[%s]", m[0]);// prints [bcd]    writefln("[%s]", m[1]);// prints [c]}else{    writeln("no match");//writeln(m.post); // Error: undefined identifier 'm'}//writeln(m.pre);      // Error: undefined identifier 'm'

While Statement

WhileStatement:while (IfCondition)ScopeStatement

AWhile Statement implements a simple loop.

If theIfCondition is anExpression, it is evaluated and must have a typethat can beconverted to a boolean.If it'strue theScopeStatement is executed.After theScopeStatement is executed, theExpression is evaluated again, andiftrue theScopeStatement is executed again. This continues until theExpressionevaluates tofalse.

int i = 0;while (i < 10){    foo(i);    ++i;}

If theIfCondition declares a variable,it is initialized and evaluated on every loop iteration.

ABreakStatement will exit the loop.

AContinueStatement will transfer directly to evaluatingIfCondition again.

Do Statement

DoStatement:doScopeStatement while (Expression);

Do while statements implement simple loops.

ScopeStatement is executed. ThenExpression is evaluated and must have atype that can beconverted to a boolean.If it'strue the loop is iteratedagain. This continues until theExpression evaluates tofalse.

int i = 0;do{    foo(i);}while (++i < 10);

ABreakStatement will exit the loop. AContinueStatementwill transfer directly to evaluatingExpression again.

For Statement

For statements implement loops with initialization, test, and incrementclauses.

ForStatement:for (InitializeTestopt;Incrementopt)ScopeStatement
Initialize:;NoScopeNonEmptyStatement
Test:Expression
Increment:Expression

Initialize is executed.Test is evaluated and must have a type that can beconverted to a boolean. IfTest istrue theScopeStatement is executed. After execution,Increment is executed. ThenTest is evaluated again, and iftrue theScopeStatement is executed again. This continues until theTest evaluates tofalse.

ABreakStatement will exit the loop. AContinueStatement will transfer directly to theIncrement.

AForStatement creates a new scope. IfInitialize declares a variable, that variable's scope extends throughScopeStatement. For example:

for (int i = 0; i < 10; i++)    foo(i);
is equivalent to:
{int i;for (i = 0; i < 10; i++)        foo(i);}

ScopeStatement cannot be an empty statement:

for (int i = 0; i < 10; i++)    ;// illegal
Use instead:
for (int i = 0; i < 10; i++){}

Initialize may be just;.Test may be omitted, and if so, it is treated as if it evaluated totrue.

Best Practices: Consider replacingForStatements withForeach Statements orForeach Range Statements. Foreach loops are easier to understand, less prone to error, and easier to refactor.

Foreach Statement

Aforeach statement iterates a series of values.

AggregateForeach:Foreach(ForeachTypeList;ForeachAggregate)
ForeachStatement:AggregateForeachNoScopeNonEmptyStatement
Foreach:foreachforeach_reverse
ForeachTypeList:ForeachTypeForeachType,ForeachTypeList
ForeachType:ForeachTypeAttributesoptBasicTypeDeclaratorForeachTypeAttributesoptIdentifierForeachTypeAttributesoptaliasIdentifier
ForeachTypeAttributes:ForeachTypeAttributeForeachTypeAttributeForeachTypeAttributes
ForeachTypeAttribute:enumrefscopeTypeCtor
ForeachAggregate:Expression

ForeachAggregate is evaluated. It must evaluate to an expression which is a static array, dynamic array, associative array, struct, class, delegate, or sequence. TheNoScopeNonEmptyStatement is executed, once for each element of the aggregate.

The number of variables declared inForeachTypeList depends on the kind of aggregate. The declared variables are set at the start of each iteration.

If not specified, the type of aForeachType variable can be inferred from the type of theForeachAggregate. Note thatauto is not a validForeachTypeAttribute. The twoforeach statements below are equivalent:

int[] arr = [1, 2, 3];foreach (int n; arr)    writeln(n);foreach (n; arr)// ok, n is an int    writeln(n);

The aggregate must beloop invariant, meaning that elements cannot be added or removed from it in theNoScopeNonEmptyStatement.

ABreakStatement in the body of the foreach will exit the loop. AContinueStatement will immediately start the next iteration.

Foreach over Arrays

If the aggregate is a static or dynamic array, there can be one or two variables declared. If one, then the variable is said to be thevalue, which is set successively to each element of the array. The type of the variable, if specified, must be compatible with the array element type (except for the special handling of character elements outlined below). Thevalue variable can modify array elements whendeclared withref.

If there are two variables declared, the first is said to be theindex and the second is said to be thevalue as above.index cannot be declared withref. It is set to the index of the array element on each iteration. The index type can be inferred:

char[] a = ['h', 'i'];foreach (i,char c; a){    writefln("a[%d] = '%c'", i, c);}

For a dynamic array, theindex type must be compatible withsize_t, unless the array length is statically known to fit in a smaller integer type. Static arrays may use any integral type that spans the length of the array.

Forforeach, the elements for the array are iterated over starting at index 0 and continuing to the last element of the array. Forforeach_reverse, the array elements are visited in the reverse order.

Foreach over Arrays of Characters

If the aggregate expression is a static or dynamic array ofchars,wchars, ordchars, then the type of thevalue variable can be any ofchar,wchar, ordchar. In this manner any UTF array can be decoded into any UTF type:

char[] a ="\xE2\x89\xA0".dup;// \u2260 encoded as 3 UTF-8 bytesforeach (dchar c; a){    writefln("a[] = %x", c);// prints 'a[] = 2260'}dchar[] b ="\u2260"d.dup;foreach (char c; b){    writef("%x, ", c);// prints 'e2, 89, a0, '}

Aggregates can be string literals, which can be accessed aschar,wchar, ordchar arrays:

foreach (char c;"ab"){    writefln("'%s'", c);}foreach (wchar w;"xy"){    writefln("'%s'", w);}

which would print:

'a''b''x''y'

Foreach over Associative Arrays

If the aggregate expression is anassociative array, there can be one or two variables declared. If one, then the variable is said to be thevalue, which is set to each value in the array, one by one. If the type of the variable is provided, it must implicitly convert from the associative array value type.

// value type is intint[string] userAges = ["john":30,"sue":32];foreach (ref age; userAges){    age++;}assert(userAges == ["john":31,"sue":33]);

If there are two variables declared, the first is said to be theindex and the second is said to be thevalue. The index variable type (if given) must be implicitly convertible from the key type of the associative array. The index variable is set to the key corresponding to each value in the associative array.

// index type is string, value type is doubledouble[string] aa = ["pi":3.14,"e":2.72];foreach (string s,double d; aa){    writefln("aa['%s'] = %g", s, d);}
Implementation Defined: The order in which the elements of the array are iterated over is unspecified forforeach. This is whyforeach_reverse for associative arrays is illegal.

If an index or value variable is declared asref, then any type given must match the associative array key or value type respectively, orconst-convert from it. Aref index variable must be (inferred as)const, as keys must not be mutated.

Foreach over Structs and Classes withopApply

If the aggregate expression is a struct or class object, theforeach is defined by the specialopApply member function, and theforeach_reverse behavior is defined by the specialopApplyReverse member function. These functions must each have the signature below:

OpApplyDeclaration:int opApply(scopeint delegate(OpApplyParameters)dg);
OpApplyParameters:OpApplyParameterOpApplyParameter,OpApplyParameters
OpApplyParameter:ForeachTypeAttributesoptBasicTypeDeclarator

where eachOpApplyParameter ofdg must match aForeachType in aForeachStatement, otherwise theForeachStatement will cause an error.

AnyForeachTypeAttribute cannot beenum.

To support aref iteration variable, the delegate must take aref parameter:
struct S{int opApply(scopeintdelegate(refuint n) dg);}void f(S s){foreach (refuint i; s)        i++;}
Above,opApply is still matched wheni is notref, so by using aref delegate parameter both forms are supported.

There can be multipleopApply andopApplyReverse functions - one is selected by matching each parameter ofdg to eachForeachType declared in theForeachStatement.

The body of the apply function iterates over the elements it aggregates, passing each one in successive calls to thedg delegate. The delegate return value determines whether to interrupt iteration:

The result of calling the delegate will be nonzero if theForeachStatement body executes a matchingBreakStatement,ReturnStatement, orGotoStatement whose matching label is outside theForeachStatement.

For example, consider a class that is a container for two elements:

class Foo{uint[] array;int opApply(scopeintdelegate(refuint) dg)    {foreach (e; array)        {int result = dg(e);if (result)return result;        }return 0;    }}void main(){import std.stdio;    Foo a =new Foo();    a.array = [73, 82, 2, 9];foreach (uint u; a)    {if (u < 5)break;        writeln(u);    }}

This would print:

7382
Thescope storage class on thedg parameter means that the delegate does not escape the scope of theopApply function (an example would be assigningdg to a global variable). If it cannot be statically guaranteed thatdg does not escape, a closure may be allocated for it on the heap instead of the stack.
Best Practices: Annotate delegate parameters toopApply functions withscope when possible.

Important: IfopApply catches any exceptions, ensure that those exceptions did not originate from the delegate passed toopApply. The user would expect exceptions thrown from aforeach body to both terminate the loop, and propagate outside theforeach body.

TemplateopApply

opApply can also be a templated function, which will infer the types of parameters based on theForeachStatement. For example:

struct S{import std.traits : ParameterTypeTuple;// introspection templateimport std.stdio;int opApply(Dg)(scope Dg dg)if (ParameterTypeTuple!Dg.length == 2)// foreach with 2 parameters    {        writeln(2);return 0;    }int opApply(Dg)(scope Dg dg)if (ParameterTypeTuple!Dg.length == 3)// foreach with 3 parameters    {        writeln(3);return 0;    }}void main(){foreach (int a,int b; S()) { }// calls first opApply functionforeach (int a,int b,float c; S()) { }// calls second opApply function}

Foreach over Structs and Classes with Ranges

If theForeachAggregate is a struct or class object, butopApply does not exist, thenforeach iteration will userange primitives. These primitives produce a series of elements.

The following properties and methods must be defined for theForeachAggregate:

Foreach Range Properties
PropertyPurpose
.emptyreturnstrue if no more elements
.frontreturn the leftmost element of the range
Foreach Range Methods
MethodPurpose
.popFront()move the left edge of the range right by one

A temporary variable is used for iteration, which is initialized from theForeachAggregate. The statement:

foreach (e; range) { ... }

translates to:

for (auto __r = range; !__r.empty; __r.popFront()){auto e = __r.front;    ...}

Similarly, forforeach_reverse whenopApplyReverse is not defined, range primitives will be used. The following properties and methods must be defined for theForeachAggregate:

Foreach_reverse Range Properties
PropertyPurpose
.emptyreturnstrue if no more elements
.backreturn the rightmost element of the range
Foreach_reverse Range Methods
MethodPurpose
.popBack()move the right edge of the range left by one

The statement:

foreach_reverse (e; range) { ... }

translates to:

for (auto __r = range; !__r.empty; __r.popBack()){auto e = __r.back;    ...}

TheForeachType variable may be declared withref whenfront (orback forforeach_reverse) is aRef Function:

struct R{int[] a;// range primitives:bool empty() => a.length == 0;ref front() => a[0];void popFront() { a = a[1..$]; }}R r = R([1, 2, 3]);foreach (ref e; r)    e *= 2;assert(r.a == [2, 4, 6]);

Example with a linked list:

struct Node{int i;    Node* next;}// rangestruct List{    Node* node;bool empty() {return node ==null; }refint front() {return node.i; }void popFront() { node = node.next; }}void main(){import std.stdio;auto n =new Node(1,new Node(2,null));auto r = List(n);foreach (e; r)    {        writeln(e);    }}

Multiple Element Values

Multiple loop variables are allowed if thefront property returns a type that expands to avalue sequence whose length matches the number of variables. Each variable is assigned to the corresponding value in the sequence. If there is only one variable, it will be a value sequence.

import std.stdio;// simple version of std.typecons.Tuplestruct Tuple(Types...)// takes a TypeSeq{    Types items;// ValueSeqalias itemsthis;// decay to a value sequence}string s ="hello";struct TupleRange{    size_t i;auto front() => Tuple!(size_t,char)(i, s[i]);bool empty() => i >= s.length;void popFront() { i++; }}void main(){// Tuple destructuringforeach (i, c; TupleRange())    {        writeln(i,": ", c);    }    writeln();// Tuple variableforeach (tup; TupleRange())    {        writefln("(%s, %s)", tup[0], tup[1]);    }}

See also:std.typecons.Tuple.

Foreach over Delegates

IfForeachAggregate is a delegate, the type signature and behavior of the delegate is the same as foropApply. This enables many different named looping strategies to coexist in the same class or struct.

The delegate can generate the elements on the fly:

// Custom loop implementation, that iterates over powers of 2 with// alternating sign. The foreach loop body is passed in dg.int myLoop(scopeintdelegate(int) dg){for (int z = 1; z < 128; z *= -2)    {auto ret = dg(z);// If the loop body contains a break, ret will be non-zero.if (ret != 0)return ret;    }return 0;}// Append each value in the iteration to an arrayint[] result;foreach (x; &myLoop){    result ~= x;}assert(result == [1, -2, 4, -8, 16, -32, 64, -128]);

foreach_reverse with a delegate is an error.

Foreach over Sequences

If the aggregate expression is asequence, the loop body is statically expanded once for each element. This is similar to aStaticForeachStatement on a sequence, though there is a separate scope for each expansion.

There can be one or two iteration symbols declared. If one, then it's anelement symbol representing each element in the sequence in turn.

If there are two symbols declared, the first is theindex and the second is theelement. The index symbol must be ofint,uint,long orulong type (if specified), it cannot beref, and it is set to the index of each sequence element.

Example:

import std.meta : AliasSeq;void main(){alias Seq = AliasSeq!(int,"literal", main);foreach (int i, sym; Seq)    {pragma(msg, i,": ", sym.stringof);    }}

Output:

0: int1: "literal"2: main()

Example:

void fun(){import std.meta : AliasSeq;alias values = AliasSeq!(7.4,"hi", [2,5]);foreach (sym; values)    {pragma(msg, sym," has type ",typeof(sym));    }}

Output:

7.4 has type doublehi has type string[2, 5] has type int[]

Symbol Kinds

EachForeachType declaration (withoutalias/enum) will attempt to generate:

alias orenum can be specified in aForeachType:

void main(){import std.meta : AliasSeq;int i = 1;// without `alias`, `a` would be a separate variable initialized from `i`foreach (alias a; AliasSeq!(i, i))    {        a++;    }assert(i == 3);foreach (e; AliasSeq!(i, 1))    {staticif (__traits(compiles, &e))assert(e == 3);// first e is a runtime variableelsestaticassert(e == 1);// second e is a compile-time constant    }}

Foreach Ref Parameters

ref can be used to modify the elements of theForeachAggregate. This works for containers that expose lvalue elements, andlvalue sequences.

uint[2] a = [7, 8];foreach (ref u; a){    u++;}foreach (u; a){    writeln(u);}
which would print:
89

ref cannot be applied to an array index variable.

Foreach Restrictions

The aggregate itself must not be resized, reallocated, free'd, reassigned or destructed whileforeach is iterating over the elements.

int[] a = [1, 2, 3];auto fun = { a ~= 4; };foreach (int v; a){// resizing is unspecified!    fun();    a ~= 4;    a.length += 10;// reallocating is unspecified!    a.reserve(10);// reassigning is unspecified!    a =null;    a = [5, 6];}a ~= 4;// OKa =null;// OK
auto aa = [1: 1, 2: 2];foreach (v; aa){    aa[3] = 3;// unspecified resize    aa.rehash;// unspecified reallocation    aa = [4: 4];// unspecified reassign}aa[3] = 3;// OKaa =null;// OK
Note: Resizing or reassigning a dynamic or associative array duringforeach is still@safe.

Foreach Range Statement

A foreach range statement loops over the specified range.

RangeForeach:Foreach(ForeachType;LwrExpression..UprExpression)
LwrExpression:Expression
UprExpression:Expression
ForeachRangeStatement:RangeForeachScopeStatement

ForeachType declares a variable with either an explicit type, or a common type inferred fromLwrExpression andUprExpression. TheScopeStatement is then executedn times, wheren is the result ofUprExpression-LwrExpression. IfUprExpression is less than or equal toLwrExpression, theScopeStatement is not executed.

IfForeach isforeach, then the variable is set toLwrExpression, then incremented at the end of each iteration. IfForeach isforeach_reverse, then the variable is set toUprExpression, then decremented before each iteration.LwrExpression andUprExpression are each evaluated exactly once, regardless of how many times theScopeStatement is executed.

import std.stdio;int foo(){    write("foo");return 10;}void main(){foreach (i; 0 .. foo())    {        write(i);    }}
prints:
foo0123456789

Switch Statement

A switch statement goes to one of a collection of case statements depending on the value of the switch expression.
SwitchStatement:switch (IfCondition)ScopeStatement
CaseStatement:caseArgumentList:ScopeStatementListopt
DefaultStatement:default :ScopeStatementListopt
ScopeStatementList:StatementListNoCaseNoDefault
StatementListNoCaseNoDefault:StatementNoCaseNoDefaultStatementNoCaseNoDefaultStatementListNoCaseNoDefault
StatementNoCaseNoDefault:EmptyStatementNonEmptyStatementNoCaseNoDefaultScopeBlockStatement

TheExpression from theIfCondition is evaluated. If the type of theExpression is anenum, it is (recursively) converted to itsEnumBaseType. Then, if the type is an integral, theExpression undergoesInteger Promotions. Then, the type of theExpression must be either an integral or a static or dynamic array ofchar,wchar, ordchar.

If anIdentifier= prefix is provided, a variable is declared with that name, initialized to the value and type of theExpression. Its scope extends from when it is initialized to the end of theScopeStatement.

IfTypeCtors and/or a specific type is provided forIdentifier, those are used for the variable declaration which is initialized by an implicit conversion from the value of theExpression.

The resulting value is compared against each of the case expressions. If there is a match, the corresponding case statement is transferred to.

The case expressions inArgumentList are a comma separated list of expressions. Each expression must evaluate to a compile-time value or array, or a runtime initialized const or immutable variable of integral type. Each expression must be implicitly convertible to the type of the switchExpression.

Compile-time case values must all be distinct. Const or immutable runtime variables must all have different names. If two case expressions share a value, the first case statement with that value gets control.

TheScopeStatementList introduces a new scope.

A matchingbreak statement will exit the switchBlockStatement.

A switch statement must have exactly oneDefaultStatement. If none of the case expressions match, control is transferred to the default statement.

Rationale: This makes it clear that all possible cases are intentionally handled. See also:final switch.
foreach (i; 2 .. 10){bool prime;switch (i)    {case 2, 3, 5, 7:            prime =true;break;default:            prime =false;    }    writeln(i,": ", prime);}

Case statements and default statements associated with the switch can be nested within block statements; they do not have to be in the outermost block. For example, this is allowed:

switch (i){case 1:    {case 2:    }    i++;break;default:}

Implementation Note: The compiler's code generator may assume that the case statements are sorted by frequency of use, with the most frequent appearing first and the least frequent last. Although this is irrelevant as far as program correctness is concerned, it is of performance interest.

Case Range Statement

CaseRangeStatement:caseFirstExp: .. caseLastExp:ScopeStatementListopt
FirstExp:AssignExpression
LastExp:AssignExpression

ACaseRangeStatement is a shorthand for listing a series of case statements fromFirstExp toLastExp, inclusive.

case 1: ..case 3:

The above is equivalent to:

case 1, 2, 3:

No Implicit Fall-Through

AScopeStatementList must either be empty, the last one in theswitch, or be ended by:

Simple forms of the last 2 cases above can be recognized by the compiler, but not all.

uint i;string message;switch (i){case 1:        message ="one";// ERROR: implicit fall-throughcase 2:// valid: the body is emptydefault:        message ="2 or more";// valid: no more case statements}

goto case; can be used for explicit fall-through to the nextCaseStatement:

string message;foreach (i; 1..5){switch (i)    {default:// valid: ends with `noreturn` expression evaluationthrownew Exception("unknown number");case 3:// valid: ends with 'break' (break out of the 'switch' only)            message ~="three";break;case 4:// valid: ends with 'continue' (continue the enclosing loop)            message ~="four";continue;// don't append a commacase 1:// valid: ends with 'goto' (explicit fall-through to next case.)            message ~=">";gotocase;case 2:// valid: this is the last case in the switch statement.            message ~="one or two";    }    message ~=", ";}writeln(message);

goto also supports jumping to a specific case or to the default case statement.

String Switch

Strings can be used in switch expressions. For example:

string name;...switch (name){case"fred":case"sally":        ...}

For applications like command line switch processing, this can lead to much more straightforward code, being clearer and less error prone.char,wchar anddchar strings are allowed.

Final Switch Statement

FinalSwitchStatement:final switch (IfCondition)ScopeStatement

A final switch statement is just like a switch statement, except that:

Implementation Defined: If theExpression value does not match any of theCaseRangeStatements, whether that is diagnosed at compile time or at runtime.

Continue Statement

ContinueStatement:continueIdentifieropt;

continue aborts the current iteration of its innermost enclosing loop statement, and starts the next iteration. If the enclosing loop is afor statement, itsIncrement clause is executed.

string[] words = ["OK","just","longer","words","now"];foreach (w; words){if (w.length < 4)continue;// skip writeln    writeln(w);}

Output:

justlongerwords

Ifcontinue is followed byIdentifier, theIdentifier must be the label of an enclosing loop statement, and the next iteration of that loop is executed. It is an error if there is no such statement.

outer:foreach (item; list){// try 3 timesforeach (i; 0 .. 3)    {if (item.buy())continue outer;// skip to next item        log("attempt failed");    }}

Any interveningfinally clauses are executed, and any intervening synchronization objects are released.

Note: If afinally clause executes athrow out of the finally clause, the continue target is never reached.

Break Statement

BreakStatement:breakIdentifieropt;

break exits the innermost enclosing loop orswitchstatement, resuming execution at the statement following it.

const n = 55;// find the smallest factor of nforeach (i; 2 .. n){    writeln("Trying: ", i);if (n % i == 0)    {        writeln("smallest factor is ", i);break;// stop looking    }}writeln("finished");

Output:

Trying: 2Trying: 3Trying: 4Trying: 5smallest factor is 5finished

Ifbreak is followed byIdentifier, theIdentifier must be the label of an enclosing loop orswitch statement, and that statement is exited. It is an error if there is no such statement.

// display messages cyclically until the shop is closedouter:while (true){foreach (msg; messages)    {if (shop.isClosed())break outer;// end the while loop        display(msg);    }}display("opens at 9am");

Any interveningfinally clauses are executed, and any intervening synchronization objects are released.

Note: If afinally clause executes athrow out of the finally clause, the break target is never reached.

Return Statement

ReturnStatement:returnExpressionopt;

return exits the current function and supplies itsreturn value.

Expression is required if the function specifies a return type that isnot void. TheExpression is implicitly converted to the function returntype.

AnExpression of type void is allowed if the function specifies a void return type. TheExpression will be evaluated, but nothing will be returned. This is useful in generic programming.

Before the function actually returns, any objects withscope storage duration are destroyed, any enclosingfinally clauses are executed, anyscope(exit) statements are executed, anyscope(success) statements are executed, and any enclosing synchronization objects are released.

The function will not return if any enclosingfinally clause does a return, goto or throw that exits thefinally clause.

If there is anout postcondition, that postcondition is executed after theExpression is evaluated and before the function actually returns.

int foo(int x){return x + 3;}

Goto Statement

GotoStatement:gotoIdentifier;gotodefault;gotocase;gotocaseExpression;

goto transfers to the statement labeled withIdentifier.

int x = 1;if (x)goto L1;    x = 3;L1:assert(x == 1);

The second form,goto default;, transfers to the innermostDefaultStatement of an enclosingSwitchStatement.

The third form,goto case;, transfers to the nextCaseStatement of the innermost enclosingSwitchStatement.

The fourth form,goto caseExpression;, transfers to theCaseStatement of the innermost enclosingSwitchStatement with a matchingExpression.

int x = 5;switch (x){case 3:        writeln("case 3");gotocase;case 4:        writeln("case 4");gotodefault;case 5:        writeln("case 5");gotocase 4;default:        writeln("default");break;}

Any intervening finally clauses are executed, along with releasing anyintervening synchronization mutexes.

It is illegal for aGotoStatement to be used to skip initializations within its containing scope.

Best Practices: Prefer using a higher-level control-flow construct or a labelledBreakStatement/ContinueStatement rather than thegoto Identifier; form.

With Statement

Thewith statement is a way to simplify repeated references to the sameobject.

WithStatement:with(Expression)ScopeStatementwith(Symbol)ScopeStatementwith(TemplateInstance)ScopeStatement
whereExpression evaluates to one of:
Within the with body the referenced object is searched first for identifier symbols.
enum E { A, B }void test(E e){with (e)// affects the switch statementswitch (e)    {case A:// no need for E.Acase B:default:break;    }}

Below, ifident is a member of the type ofexpression, theWithStatement:

with (expression){    ...    ident;}
is semantically equivalent to:
(autoref tmp){    ...    tmp.ident;}(expression);

Note thatExpression only gets evaluated once and is not copied. The with statement does not change whatthis orsuper refer to.

ForSymbol which is a scope orTemplateInstance, the corresponding scope is searched when looking up symbols. For example:

struct Foo{alias Y =int;}...Y y;// error, Y undefinedwith (Foo){    Y y;// same as Foo.Y y;}

Use ofwith object symbols that shadow local symbols with the same identifier are not allowed. This is to reduce the risk of inadvertent breakage of with statements when new members are added to the object declaration.

struct S{float x;}void main(){int x;    S s;with (s)    {        x++;// error, shadows the int x declaration    }}

In nestedWithStatements, the inner-most scope takes precedence. If a symbol cannot be resolved at the inner-most scope, resolution is forwarded incrementally up the scope hierarchy.

import std.stdio;struct Foo{void f() { writeln("Foo.f"); }}struct Bar{void f() { writeln("Bar.f"); }}struct Baz{// f() is not implemented}void f(){    writeln("f");}void main(){    Foo foo;    Bar bar;    Baz baz;    f();// prints "f"with(foo)    {        f();// prints "Foo.f"with(bar)        {            f();// prints "Bar.f"with(baz)            {                f();// prints "Bar.f".  `Baz` does not implement `f()` so// resolution is forwarded to `with(bar)`'s scope            }        }with(baz)        {            f();// prints "Foo.f".  `Baz` does not implement `f()` so// resolution is forwarded to `with(foo)`'s scope        }    }with(baz)    {        f();// prints "f".  `Baz` does not implement `f()` so// resolution is forwarded to `main`'s scope. `f()` is// not implemented in `main`'s scope, so resolution is// subsequently forward to module scope.    }}

Synchronized Statement

The synchronized statement wraps a statement with mutex locking and unlocking to synchronize access among multiple threads.

SynchronizedStatement:synchronizedScopeStatementsynchronized (Expression)ScopeStatement

A synchronized statement withoutExpression allows only one thread at a time to executeScopeStatement by locking a mutex. A global mutex is created, one per synchronized statement. Different synchronized statements will have different global mutexes.

If there is anExpression, it must evaluate to either an Object or an instance of aninterface, in which case it is cast to the Object instance that implemented that interface. The mutex used is specific to that Object instance, and is shared by all synchronized statements referring to that instance. If the object's mutex is already locked when reaching the synchronized statement, it will block every thread until that mutex is unlocked by other code.

void work();void f(Object o){synchronized (o) work();}void g(Object o){synchronized (o) work();}

Iff andg are called by different threads but with the same argument, thework calls cannot execute simultaneously. If the(o) part of thesynchronized statements is removed in one or both functions, then bothwork calls could execute simultaneously, because they would be protected by different mutexes.

The synchronization gets released even ifScopeStatement terminates with an exception, goto, or return.

This implements a standard critical section.

Synchronized statements support recursive locking; that is, a function wrapped in synchronized is allowed to recursively call itself and the behavior will be as expected: The mutex will be locked and unlocked as many times as there is recursion.

See alsosynchronized classes.

Try Statement

Exception handling is done with the try-catch-finally statement.

TryStatement:tryScopeStatementCatchestryScopeStatementCatchesFinallyStatementtryScopeStatementFinallyStatement
Catches:CatchCatchCatches
Catch:catch (CatchParameter)NoScopeNonEmptyStatement
CatchParameter:BasicTypeIdentifieropt
FinallyStatement:finallyNoScopeNonEmptyStatement

CatchParameter declares a variable v of type T, where T is Throwable or derived from Throwable. v is initialized by the throw expression if T is of the same type or a base class of the throw expression. The catch clause will be executed if the exception object is of type T or derived from T.

If just type T is given and no variable v, then the catch clause is still executed.

It is an error if anyCatchParameter type T1 hides a subsequentCatch with type T2, i.e. it is an error if T1 is the same type as or a base class of T2.

TheFinallyStatement is always executed, whether thetryScopeStatement exits with a goto, break, continue, return, exception, or fall-through.

If an exception is raised in theFinallyStatement and is not caught before the original exception is caught, it is chained to the previous exception via thenext member ofThrowable. Note that, in contrast to most other programming languages, the new exception does not replace the original exception. Instead, later exceptions are regarded as 'collateral damage' caused by the first exception. The original exception must be caught, and this results in the capture of the entire chain.

Thrown objects derived from theError class are treated differently. They bypass the normal chaining mechanism, such that the chain can only be caught by catching the firstError. In addition to the list of subsequent exceptions,Error also contains a pointer that points to the original exception (the head of the chain) if a bypass occurred, so that the entire exception history is retained.

import std.stdio;int main(){try    {try        {thrownew Exception("first");        }finally        {            writeln("finally");thrownew Exception("second");        }    }catch (Exception e)    {        writefln("catch %s", e.msg);    }    writeln("done");return 0;}
prints:
finallycatch firstdone

AFinallyStatement may not exit with a goto, break, continue, or return; nor may it be entered with a goto.

AFinallyStatement may not contain anyCatches. This restriction may be relaxed in future versions.

Scope Guard Statement

ScopeGuardStatement:scope ( exit )NonEmptyOrScopeBlockStatementscope ( success )NonEmptyOrScopeBlockStatementscope ( failure )NonEmptyOrScopeBlockStatement

TheScopeGuardStatement executesNonEmptyOrScopeBlockStatement at the close of thecurrent scope, rather than at the point where theScopeGuardStatementappears.scope(exit) executesNonEmptyOrScopeBlockStatement when the scope exits normallyor when it exits due to exception unwinding.scope(failure) executesNonEmptyOrScopeBlockStatement when the scope exits due to exception unwinding.scope(success) executesNonEmptyOrScopeBlockStatement when the scope exits normally.

If there are multipleScopeGuardStatements in a scope, they will be executed in the reverse lexical order in which they appear. If any scope instances are to be destroyed upon the close of the scope, their destructions will be interleaved with theScopeGuardStatements in the reverse lexical order in which they appear.

write("1");{    write("2");scope(exit) write("3");scope(exit) write("4");    write("5");}writeln();
writes:
12543
{scope(exit) write("1");scope(success) write("2");scope(exit) write("3");scope(success) write("4");}writeln();
writes:
4321
struct Foo{this(string s) { write(s); }    ~this() { write("1"); }}try{scope(exit) write("2");scope(success) write("3");    Foo f = Foo("0");scope(failure) write("4");thrownew Exception("msg");scope(exit) write("5");scope(success) write("6");scope(failure) write("7");}catch (Exception e){}writeln();
writes:
0412
Ascope(exit) orscope(success) statement may not exit with a throw, goto, break, continue, or return; nor may it be entered with a goto. Ascope(failure) statement may not exit with a return.
import std.stdio;int foo(){scope(exit) writeln("Inside foo()");return bar();}int bar(){    writeln("Inside bar()");return 0;}int main(){    foo();return 0;}
writes:
Inside bar()Inside foo()

Catching C++ Class Objects

On many platforms, catching C++ class objects is supported. Catching C++ objects and D objects cannot both be done in the sameTryStatement. Upon exit from theCatch, any destructors for the C++ object will be run and the storage used for it reclaimed. C++ objects cannot be caught in@safe code.

Asm Statement

Inline assembler is supported with the asm statement:

AsmStatement:asmFunctionAttributesopt{AsmInstructionListopt}
AsmInstructionList:AsmInstruction;AsmInstruction;AsmInstructionList

An asm statement enables the direct use of assembly language instructions.This makes it easy to obtain direct access to special CPU features withoutresorting to an external assembler. The D compiler will take care of thefunction calling conventions, stack setup, etc.

The format of the instructions is, of course, highly dependent on the native instruction set of the target CPU, and so isimplementation defined. But, the format will follow the following conventions:

These rules exist to ensure that D source code can be tokenized independently of syntactic or semantic analysis.

For example, for the Intel Pentium:

int x = 3;asm{    mov EAX,x;// load x and put it in register EAX}

Inline assembler can be used to access hardware directly:

int gethardware(){asm    {        mov EAX, dword ptr 0x1234;    }}

For some D implementations, such as a translator from D to C, an inlineassembler makes no sense, and need not be implemented. The version statement canbe used to account for this:

version (D_InlineAsm_X86){asm    {        ...    }}else{/* ... some workaround ... */}

Semantically consecutiveAsmStatements shall not have any other instructions (such as register save or restores) inserted between them by the compiler.

Pragma Statement

SeePragmaStatement

.

Mixin Statement

MixinStatement:mixin(ArgumentList);

EachAssignExpression in theArgumentList is evaluated at compile time, and the result must be representable as a string. The resulting strings are concatenated to form a string. The text contents of the string must be compilable as a validStatementList, and is compiled as such.

import std.stdio;void main(){int i = 0;mixin("        int x = 3;        for (; i < 3; i++)            writeln(x + i, i);        ");// okenum s ="int y;";mixin(s);// ok    y = 4;// ok, mixin declared y    string t ="y = 3;";//mixin(t);  // error, t is not evaluatable at compile time//mixin("y =") 4; // error, string must be complete statementmixin("y =" ~"4;");// okmixin("y =", 2+2,";");// ok}
Expressions
Arrays
)
Copyright © 1999-2025 by theD Language Foundation | Page generated byDdoc on Fri Oct 10 22:16:57 2025

[8]ページ先頭

©2009-2025 Movatter.jp