$(DDOC $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(D_S Type Qualifiers, $(DDOC_BLANKLINE ) $(HEADERNAV_TOC $(HEADERNAV_ITEM const_and_immutable, Const and Immutable) $(HEADERNAV_ITEM immutable_storage_class, Immutable Storage Class) $(HEADERNAV_ITEM const_storage_class, Const Storage Class) $(HEADERNAV_ITEM immutable_type, Immutable Type) $(HEADERNAV_ITEM creating_immutable_data, Creating Immutable Data) $(HEADERNAV_ITEM removing_with_cast, Removing Immutable or Const with a Cast) $(HEADERNAV_ITEM immutable_member_functions, Immutable Member Functions) $(HEADERNAV_ITEM const_type, Const Type) $(HEADERNAV_ITEM const_member_functions, Const Member Functions) $(HEADERNAV_SUBITEMS inout, Inout, $(HEADERNAV_ITEM matching-an-inout-parameter, Matching an inout Parameter) ) $(HEADERNAV_SUBITEMS shared, Shared, $(HEADERNAV_ITEM shared_cast, Casting) $(HEADERNAV_ITEM shared_global, Shared Global Variables) ) $(HEADERNAV_ITEM combining_qualifiers, Combining Qualifiers) $(HEADERNAV_SUBITEMS implicit_qualifier_conversions, Implicit Qualifier Conversions, $(HEADERNAV_ITEM unique-expressions, Unique Expressions) ) ) $(DDOC_BLANKLINE ) $(P Type qualifiers modify a type by applying a $(GLINK2 type, TypeCtor). $(I TypeCtor)s are: $(D const), $(D immutable), $(D shared), and $(D inout). Each applies transitively to all subtypes. ) $(DDOC_BLANKLINE )

$(LNAME2 const_and_immutable, Const and Immutable)

$(DDOC_BLANKLINE ) $(P When examining a data structure or interface, it is very helpful to be able to easily tell which data can be expected to not change, which data might change, and who may change that data. This is done with the aid of the language typing system. Data can be marked as const or immutable, with the default being changeable (or $(I mutable)). ) $(DDOC_BLANKLINE ) $(P $(D immutable) applies to data that cannot change. Immutable data values, once constructed, remain the same for the duration of the program's execution. Immutable data can be placed in ROM (Read Only Memory) or in memory pages marked by the hardware as read only. Since immutable data does not change, it enables many opportunities for program optimization, and has applications in functional style programming. ) $(DDOC_BLANKLINE ) $(P $(D const) applies to data that cannot be changed by the const reference to that data. It may, however, be changed by another reference to that same data. Const finds applications in passing data through interfaces that promise not to modify them. ) $(DDOC_BLANKLINE ) $(P Both immutable and const are $(I transitive), which means that any data reachable through an immutable reference is also immutable, and likewise for const. ) $(DDOC_BLANKLINE )

$(LNAME2 immutable_storage_class, Immutable Storage Class)

$(DDOC_BLANKLINE ) $(P The simplest immutable declarations use it as a storage class. It can be used to declare manifest constants. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable) $(D_KEYWORD int) x = 3; $(D_COMMENT // x is set to 3 )x = 4; $(D_COMMENT // error, x is immutable )$(D_KEYWORD char)[x] s; $(D_COMMENT // s is an array of 3 chars )) $(DDOC_BLANKLINE ) $(P The type can be inferred from the initializer: ) $(D_CODE $(D_KEYWORD immutable) y = 4; $(D_COMMENT // y is of type int )y = 5; $(D_COMMENT // error, y is immutable )) $(DDOC_BLANKLINE ) $(P If the initializer is not present, the immutable can be initialized from the corresponding constructor: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable) $(D_KEYWORD int) z; $(D_KEYWORD void) test() { z = 3; $(D_COMMENT // error, z is immutable )} $(D_KEYWORD static) $(D_KEYWORD this)() { z = 3; $(D_COMMENT // ok, can set immutable that doesn't ) $(D_COMMENT // have static initializer )} ) $(P The initializer for a non-local immutable declaration must be evaluatable at compile time: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) foo($(D_KEYWORD int) f) { $(D_KEYWORD return) f * 3; } $(D_KEYWORD int) i = 5; $(D_KEYWORD immutable) x = 3 * 4; $(D_COMMENT // ok, 12 )$(D_KEYWORD immutable) y = i + 1; $(D_COMMENT // error, cannot evaluate at compile time )$(D_KEYWORD immutable) z = foo(2) + 1; $(D_COMMENT // ok, foo$(LPAREN)2$(RPAREN ) can be evaluated at compile time, 7 )) $(DDOC_BLANKLINE ) $(P The initializer for a non-static local immutable declaration is evaluated at run time: ) $(D_CODE $(D_KEYWORD int) foo($(D_KEYWORD int) f) { $(D_KEYWORD immutable) x = f + 1; $(D_COMMENT // evaluated at run time ) x = 3; $(D_COMMENT // error, x is immutable )} ) $(DDOC_BLANKLINE ) $(P Because immutable is transitive, data referred to by an immutable is also immutable: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable) $(D_KEYWORD char)[] s = $(D_STRING "foo"); s[0] = 'a'; $(D_COMMENT // error, s refers to immutable data )s = $(D_STRING "bar"); $(D_COMMENT // error, s is immutable )) $(DDOC_BLANKLINE ) $(P Immutable declarations can appear as lvalues, i.e. they can have their address taken, and occupy storage. ) $(DDOC_BLANKLINE )

$(LNAME2 const_storage_class, Const Storage Class)

$(DDOC_BLANKLINE ) $(P A const declaration is exactly like an immutable declaration, with the following differences: ) $(DDOC_BLANKLINE ) $(UL $(LI Any data referenced by the const declaration cannot be changed from the const declaration, but it might be changed by other references to the same data.) $(DDOC_BLANKLINE ) $(LI The type of a const declaration is itself const.) ) $(DDOC_BLANKLINE ) $(COMMENT $(TABLE $(DDOC_BLANKLINE ) $(TR $(TH $(NBSP )) $(TH AddrOf) $(TH CTFEInit) $(TH Static) $(TH Field) $(TH Stack) $(TH Ctor)) $(DDOC_BLANKLINE ) $(TR $(TD $(NBSP )) $(TD Can the address be taken?) $(TD Is compile time function evaluation done on the initializer?) $(TD allocated as static data?) $(TD allocated as a per-instance field?) $(TD allocated on the stack?) $(TD Can the variable be assigned to in a constructor?) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(TR $(TH Global data)) $(DDOC_BLANKLINE ) $(TR $(CODE const T x;) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(GREEN Yes))) $(TR $(CODE const T x = 3;) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No))) $(TR $(CODE static const T x;) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(GREEN Yes))) $(TR $(CODE static const T x = 3;) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No))) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(TR $(TH Class Members)) $(DDOC_BLANKLINE ) $(TR $(CODE const T x;) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(GREEN Yes))) $(TR $(CODE const T x = 3;) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No))) $(TR $(CODE static const T x;) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(GREEN Yes))) $(TR $(CODE static const T x = 3;) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No))) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(TR $(TH Local Variables)) $(DDOC_BLANKLINE ) $(TR $(CODE const T x;) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No))) $(TR $(CODE const T x = 3;) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No))) $(TR $(CODE static const T x;) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No))) $(TR $(CODE static const T x = 3;) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No))) $(DDOC_BLANKLINE ) $(TR $(TH Function Parameters)) $(DDOC_BLANKLINE ) $(TR $(CODE const T x;) $(TD $(GREEN Yes)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(RED No)) $(TD $(GREEN Yes)) $(TD $(RED No))) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(P Notes:) $(DDOC_BLANKLINE ) $(OL $(LI If CTFEInit is true, then the initializer can also be used for constant folding.) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(TABLE Template Argument Deduced Type $(TR $(TH $(NBSP )) $(TH mutable $(CODE T)) $(CODE const(T)) $(CODE immutable(T))) $(TR $(CODE foo(U)) $(GREEN $(CODE T)) $(GREEN $(CODE T)) $(GREEN $(CODE T))) $(TR $(CODE foo(U:U)) $(GREEN $(CODE T)) $(GREEN $(CODE const(T))) $(GREEN $(CODE immutable(T)))) $(TR $(CODE foo(U:const(U))) $(ORANGE $(CODE T)) $(GREEN $(CODE T)) $(ORANGE $(CODE T))) $(TR $(CODE foo(U:immutable(U))) $(TD $(RED no match)) $(TD $(RED no match)) $(GREEN $(CODE T))) ) $(DDOC_BLANKLINE ) $(P Where:) $(DDOC_BLANKLINE ) $(TABLE $(TR $(TD $(GREEN green)) $(TD exact match)) $(TR $(TD $(ORANGE orange)) $(TD implicit conversion)) ) ) $(DDOC_BLANKLINE )

$(LNAME2 immutable_type, Immutable Type)

$(DDOC_BLANKLINE ) $(P Data that will never change its value can be typed as immutable. The immutable keyword can be used as a $(I type qualifier): ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable)($(D_KEYWORD char))[] s = $(D_STRING "hello"); ) $(DDOC_BLANKLINE ) $(P The immutable applies to the type within the following parentheses. So, while $(CODE s) can be assigned new values, the contents of $(CODE s[]) cannot be: ) $(DDOC_BLANKLINE ) $(D_CODE s[0] = 'b'; $(D_COMMENT // error, s[] is immutable )s = $(D_KEYWORD null); $(D_COMMENT // ok, s itself is not immutable )) $(DDOC_BLANKLINE ) $(P Immutability is transitive, meaning it applies to anything that can be referenced from the immutable type: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable)($(D_KEYWORD char)*)** p = ...; p = ...; $(D_COMMENT // ok, p is not immutable )*p = ...; $(D_COMMENT // ok, *p is not immutable )**p = ...; $(D_COMMENT // error, **p is immutable )***p = ...; $(D_COMMENT // error, ***p is immutable )) $(DDOC_BLANKLINE ) $(P Immutable used as a storage class is equivalent to using immutable as a type qualifier for the entire type of a declaration:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable) $(D_KEYWORD int) x = 3; $(D_COMMENT // x is typed as immutable$(LPAREN)int$(RPAREN ) )$(D_KEYWORD immutable)($(D_KEYWORD int)) y = 3; $(D_COMMENT // y is immutable )) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 creating_immutable_data, Creating Immutable Data)

$(DDOC_BLANKLINE ) $(P The first way is to use a literal that is already immutable, such as string literals. String literals are always immutable. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD auto) s = $(D_STRING "hello"); $(D_COMMENT // s is immutable$(LPAREN)char$(RPAREN )[5] )$(D_KEYWORD char)[] p = $(D_STRING "world"); $(D_COMMENT // error, cannot implicitly convert immutable ) $(D_COMMENT // to mutable )) $(DDOC_BLANKLINE ) $(P The second way is to cast data to immutable. When doing so, it is up to the programmer to ensure that any mutable references to the same data are not used to modify the data after the cast. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD char)[] s = ['a']; s[0] = 'b'; $(D_COMMENT // ok )$(D_KEYWORD immutable)($(D_KEYWORD char))[] p = $(D_KEYWORD cast)($(D_KEYWORD immutable))s; $(D_COMMENT // ok, if data is not mutated ) $(D_COMMENT // through s anymore )s[0] = 'c'; $(D_COMMENT // undefined behavior )$(D_KEYWORD immutable)($(D_KEYWORD char))[] q = $(D_KEYWORD cast)($(D_KEYWORD immutable))s.dup; $(D_COMMENT // always ok, unique reference ) $(D_KEYWORD char)[][] s2 = [['a', 'b'], ['c', 'd']]; $(D_KEYWORD immutable)($(D_KEYWORD char)[][]) p2 = $(D_KEYWORD cast)($(D_KEYWORD immutable))s2.dup; $(D_COMMENT // dangerous, only the first ) $(D_COMMENT // level of elements is unique )s2[0] = ['x', 'y']; $(D_COMMENT // ok, doesn't affect p2 )s2[1][0] = 'z'; $(D_COMMENT // undefined behavior )$(D_KEYWORD immutable)($(D_KEYWORD char)[][]) q2 = [s2[0].dup, s2[1].dup]; $(D_COMMENT // always ok, unique references )) $(DDOC_BLANKLINE ) $(P The $(CODE .idup) property is a convenient way to create an immutable copy of an array: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD auto) p = s.idup; p[0] = ...; $(D_COMMENT // error, p[] is immutable )) $(DDOC_BLANKLINE )

$(LNAME2 removing_with_cast, Removing Immutable or Const with a Cast)

$(DDOC_BLANKLINE ) $(P An immutable or const type qualifier can be removed with a cast: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable) $(D_KEYWORD int)* p = ...; $(D_KEYWORD int)* q = $(D_KEYWORD cast)($(D_KEYWORD int)*)p; ) $(DDOC_BLANKLINE ) $(P This does not mean, however, that one can change the data: ) $(DDOC_BLANKLINE ) $(D_CODE *q = 3; $(D_COMMENT // allowed by compiler, but result is undefined behavior )) $(DDOC_BLANKLINE ) $(P The ability to cast away immutable-correctness is necessary in some cases where the static typing is incorrect and not fixable, such as when referencing code in a library one cannot change. Casting is, as always, a blunt and effective instrument, and when using it to cast away immutable-correctness, one must assume the responsibility to ensure the immutability of the data, as the compiler will no longer be able to statically do so. ) $(DDOC_BLANKLINE ) $(UNDEFINED_BEHAVIOR casting away a const qualifier and then mutating it, even when the referenced data is mutable. This is so that compilers and programmers can make assumptions based on const alone. For example, here it may be assumed that $(D f) does not alter $(D x): ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) f($(D_KEYWORD const) $(D_KEYWORD int)* a); $(D_KEYWORD void) main() { $(D_KEYWORD int) x = 1; f(&x); $(D_KEYWORD assert)(x == 1); $(D_COMMENT // guaranteed to hold )} ) $(DDOC_BLANKLINE )

$(LNAME2 immutable_member_functions, Immutable Member Functions)

$(DDOC_BLANKLINE ) $(P Immutable member functions are guaranteed that the object and anything referred to by the $(DDSUBLINK spec/expression, this, this reference) is immutable. They are declared as: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) x; $(D_KEYWORD void) foo() $(D_KEYWORD immutable) { x = 4; $(D_COMMENT // error, x is immutable ) $(D_KEYWORD this).x = 4; $(D_COMMENT // error, x is immutable ) } } ) $(P Note that using $(D_KEYWORD immutable) on the left hand side of a method does not apply to the return type: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD immutable) $(D_KEYWORD int)[] bar() $(D_COMMENT // bar is still immutable, return type is not! ) { } } ) $(P To make the return type $(D_KEYWORD immutable), surround it with parentheses: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD immutable)($(D_KEYWORD int)[]) bar() $(D_COMMENT // bar is now mutable, return type is immutable. ) { } } ) $(P To make both the return type and the method $(D_KEYWORD immutable), write: ) $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD immutable)($(D_KEYWORD int)[]) bar() $(D_KEYWORD immutable) { } } ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 const_type, Const Type)

$(DDOC_BLANKLINE ) $(P Const types are like immutable types, except that const forms a read-only $(I view) of data. Other aliases to that same data may change it at any time. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 const_member_functions, Const Member Functions)

$(DDOC_BLANKLINE ) $(P Const member functions are functions that are not allowed to change any part of the object through the member function's $(DDSUBLINK spec/expression, this, this reference). ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 inout, Inout)

$(DDOC_BLANKLINE ) $(P Functions that differ only in whether the parameters are mutable, const or immutable, and have corresponding mutable, const or immutable return types, can be combined into one function using the $(D inout) type constructor. Consider the following overload set: ) $(D_CODE $(D_KEYWORD int)[] slice($(D_KEYWORD int)[] a, $(D_KEYWORD int) x, $(D_KEYWORD int) y) { $(D_KEYWORD return) a[x .. y]; } $(D_KEYWORD const)($(D_KEYWORD int))[] slice($(D_KEYWORD const)($(D_KEYWORD int))[] a, $(D_KEYWORD int) x, $(D_KEYWORD int) y) { $(D_KEYWORD return) a[x .. y]; } $(D_KEYWORD immutable)($(D_KEYWORD int))[] slice($(D_KEYWORD immutable)($(D_KEYWORD int))[] a, $(D_KEYWORD int) x, $(D_KEYWORD int) y) { $(D_KEYWORD return) a[x .. y]; } ) $(DDOC_BLANKLINE ) $(P The code generated by each of these functions is identical. The $(D_KEYWORD inout) type constructor can combine them into one function:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD inout)($(D_KEYWORD int))[] slice($(D_KEYWORD inout)($(D_KEYWORD int))[] a, $(D_KEYWORD int) x, $(D_KEYWORD int) y) { $(D_KEYWORD return) a[x .. y]; } ) ) $(DDOC_BLANKLINE ) $(P The $(D_KEYWORD inout) keyword forms a wildcard that stands in for mutable, const, immutable, inout, or inout const. When calling the function, the inout state of the return type is changed to match that of the argument type passed to the inout parameter. ) $(DDOC_BLANKLINE ) $(P inout can also be used as a type constructor inside a function that has a parameter declared with inout. The inout state of a type declared with inout is changed to match that of the argument type passed to the inout parameter: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD inout)($(D_KEYWORD int))[] asymmetric($(D_KEYWORD inout)($(D_KEYWORD int))[] input_data) { $(D_KEYWORD inout)($(D_KEYWORD int))[] r = input_data; $(D_KEYWORD while) (r.length > 1 && r[0] == r[$-1]) r = r[1..$-1]; $(D_KEYWORD return) r; } ) ) $(DDOC_BLANKLINE ) $(P 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. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD void) f($(D_KEYWORD inout) $(D_KEYWORD int)* ptr) { $(D_KEYWORD const) $(D_KEYWORD int)* p = ptr; $(D_KEYWORD int)* q = ptr; $(D_COMMENT // error ) $(D_KEYWORD immutable) $(D_KEYWORD int)* r = ptr; $(D_COMMENT // error )} ) ) $(DDOC_BLANKLINE )

$(LNAME2 matching-an-inout-parameter, Matching an inout Parameter)

$(DDOC_BLANKLINE ) $(P A set of arguments to a function with inout parameters is considered a match if any inout argument types match exactly, or:) $(DDOC_BLANKLINE ) $(OL $(LI No argument types are composed of inout types.) $(LI A mutable, const or immutable argument type can be matched against each corresponding parameter inout type.) ) $(DDOC_BLANKLINE ) $(P If such a match occurs, inout is considered the common qualifier of the matched qualifiers. If more than two parameters exist, the common qualifier calculation is recursively applied. ) $(DDOC_BLANKLINE ) $(TABLE2 Common qualifier of the two type qualifiers, $(TROW , $(I mutable), $(D const), $(D immutable), $(D inout), $(D inout const)) $(TROW $(I mutable) $(LPAREN)= m$(RPAREN ), m, c, c, c, c) $(TROW $(D const) $(LPAREN)= c$(RPAREN ), c, c, c, c, c) $(TROW $(D immutable) $(LPAREN)= i$(RPAREN ), c, c, i, wc, wc) $(TROW $(D inout) $(LPAREN)= w$(RPAREN ), c, c, wc, w, wc) $(TROW $(D inout const) $(LPAREN)= wc$(RPAREN ), c, c, wc, wc, wc) ) $(DDOC_BLANKLINE ) $(P The inout in the return type is then rewritten to match the inout qualifiers:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int)[] ma; $(D_KEYWORD const)($(D_KEYWORD int))[] ca; $(D_KEYWORD immutable)($(D_KEYWORD int))[] ia; $(D_KEYWORD inout)($(D_KEYWORD int))[] foo($(D_KEYWORD inout)($(D_KEYWORD int))[] a) { $(D_KEYWORD return) a; } $(D_KEYWORD void) test1() { $(D_COMMENT // inout matches to mutable, so inout$(LPAREN)int$(RPAREN )[] is ) $(D_COMMENT // rewritten to int[] ) $(D_KEYWORD int)[] x = foo(ma); $(D_COMMENT // inout matches to const, so inout$(LPAREN)int$(RPAREN )[] is ) $(D_COMMENT // rewritten to const$(LPAREN)int$(RPAREN )[] ) $(D_KEYWORD const)($(D_KEYWORD int))[] y = foo(ca); $(D_COMMENT // inout matches to immutable, so inout$(LPAREN)int$(RPAREN )[] is ) $(D_COMMENT // rewritten to immutable$(LPAREN)int$(RPAREN )[] ) $(D_KEYWORD immutable)($(D_KEYWORD int))[] z = foo(ia); } $(D_KEYWORD inout)($(D_KEYWORD const)($(D_KEYWORD int)))[] bar($(D_KEYWORD inout)($(D_KEYWORD int))[] a) { $(D_KEYWORD return) a; } $(D_KEYWORD void) test2() { $(D_COMMENT // inout matches to mutable, so inout$(LPAREN)const$(LPAREN)int$(RPAREN )$(RPAREN )[] is ) $(D_COMMENT // rewritten to const$(LPAREN)int$(RPAREN )[] ) $(D_KEYWORD const)($(D_KEYWORD int))[] x = bar(ma); $(D_COMMENT // inout matches to const, so inout$(LPAREN)const$(LPAREN)int$(RPAREN )$(RPAREN )[] is ) $(D_COMMENT // rewritten to const$(LPAREN)int$(RPAREN )[] ) $(D_KEYWORD const)($(D_KEYWORD int))[] y = bar(ca); $(D_COMMENT // inout matches to immutable, so inout$(LPAREN)int$(RPAREN )[] is ) $(D_COMMENT // rewritten to immutable$(LPAREN)int$(RPAREN )[] ) $(D_KEYWORD immutable)($(D_KEYWORD int))[] z = bar(ia); } ) ) $(DDOC_BLANKLINE ) $(P $(B Note:) Shared types cannot be matched with inout. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 shared, Shared)

$(DDOC_BLANKLINE ) $(P Mutable data that is meant to be shared among multiple threads should be declared with the shared qualifier. This prevents unsynchronized reading and writing to the data, which would otherwise cause data races. The shared type attribute is transitive (like const and immutable). ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD shared) $(D_KEYWORD int) x; $(D_KEYWORD shared)($(D_KEYWORD int))* p = &x; $(D_COMMENT //int* q = p; // error, q is not shared )) ) $(P For basic data types, reading and writing can normally be done with atomic operations. Use $(MREF core, atomic) for portability: ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD import) core.atomic; $(D_KEYWORD shared) $(D_KEYWORD int) x; $(D_KEYWORD void) fun() { $(D_COMMENT //x++; // error, use atomicOp instead ) x.atomicOp!$(D_STRING "+=")(1); } ) ) $(P $(RED Warning:) An individual read or write operation on shared data is not an error yet by default. To detect these, use the -preview=nosharedaccess compiler option. Normal initialization is allowed without an error.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) core.atomic; $(D_KEYWORD int) y; $(D_KEYWORD shared) $(D_KEYWORD int) x = y; $(D_COMMENT // OK ) $(D_COMMENT //x = 5; // write error with preview flag )x.atomicStore(5); $(D_COMMENT // OK )$(D_COMMENT //y = x; // read error with preview flag )y = x.atomicLoad(); $(D_COMMENT // OK )$(D_KEYWORD assert)(y == 5); ) ) $(DDOC_BLANKLINE )

$(LNAME2 shared_cast, Casting)

$(DDOC_BLANKLINE ) $(P When working with larger types, manual synchronization can be used. To do that, shared can be cast away for the duration while mutual exclusion has been established: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) T; $(D_KEYWORD shared) T* x; $(D_KEYWORD void) fun() { $(D_KEYWORD synchronized) { T* p = $(D_KEYWORD cast)(T*)x; $(D_COMMENT // operate on `*p` ) } } ) ) $(DDOC_BLANKLINE ) $(P An unshared reference can be cast to shared only if the source data will not be accessed for the lifetime of the cast result.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD class) C {} @trusted $(D_KEYWORD shared)(C) create() { $(D_KEYWORD auto) c = $(D_KEYWORD new) C; $(D_COMMENT // work with c without it escaping ) $(D_KEYWORD return) $(D_KEYWORD cast)($(D_KEYWORD shared))c; $(D_COMMENT // OK )} ) ) $(DDOC_BLANKLINE )

$(LNAME2 shared_global, Shared Global Variables)

$(DDOC_BLANKLINE ) $(P Global (or static) shared variables are stored in common storage which is accessible across threads. Global mutable variables are stored in thread-local storage by default.) $(DDOC_BLANKLINE ) $(P To declare global/static data to be implicitly shared across multiple threads without any compiler checks, see $(DDSUBLINK spec/attribute, gshared, __gshared). ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 combining_qualifiers, Combining Qualifiers)

$(DDOC_BLANKLINE ) $(P More than one qualifier may apply to a type. The order of application is irrelevant, for example given an unqualified type T, $(D const shared T) and $(D shared const T) are the same type. For that reason, this document depicts qualifier combinations without parentheses unless necessary and in alphabetic order.) $(DDOC_BLANKLINE ) $(P Applying a qualifier to a type that already has that qualifier is legal but has no effect, e.g. given an unqualified type T, $(D shared(const shared T)) yields the type $(D const shared T).) $(DDOC_BLANKLINE ) $(P Applying the immutable qualifier to any type (qualified or not) results in $(D immutable T). Applying any qualifier to $(D immutable T) results in immutable T. This makes immutable a fixed point of qualifier combinations and makes types such as $(D const(immutable(shared T))) impossible to create.) $(DDOC_BLANKLINE ) $(P Assuming T is an unqualified type, the graph below illustrates how qualifiers combine (combinations with immutable are omitted). For each node, applying the qualifier labeling the edge leads to the resulting type.) $(DDOC_BLANKLINE ) $(COMMENT To generate images/qualifier-combinations.{svg,eps} refer to images/qualifier-combinations.dot.) $(DDOC_BLANKLINE ) $(HTMLTAG3 center, , Qualifier combination rules ) $(LATEX {\centering \includegraphics{images/qualifier-combinations.eps} } ) $(DDOC_BLANKLINE )

$(LNAME2 implicit_qualifier_conversions, Implicit Qualifier Conversions)

$(DDOC_BLANKLINE ) $(P Values that have no mutable indirections (including structs that don't contain any field with mutable indirections) can be implicitly converted across $(I mutable), $(D const), $(D immutable), $(D const shared), $(D inout) and $(D inout shared).) $(DDOC_BLANKLINE ) $(P References to qualified objects can be implicitly converted according to the following rules:) $(DDOC_BLANKLINE ) $(COMMENT To generate images/qualifier-conversions.{svg,eps} refer to images/qualifier-conversions.dot.) $(DDOC_BLANKLINE ) $(HTMLTAG3 center, , Qualifier conversion rules ) $(LATEX {\centering \includegraphics{images/qualifier-conversions.eps} } ) $(DDOC_BLANKLINE ) $(P In the graph above, any directed path is a legal implicit conversion. No other qualifier combinations than the ones shown is valid. If a directed path exists between two sets of qualifiers, the types thus qualified are called $(GLOSSARY qualifier-convertible). The same information is shown below in tabular format:) $(DDOC_BLANKLINE ) $(TABLE_10 $(ARGS Implicit Conversion of Reference Types), $(VERTROW from/to, $(I mutable), $(D const), $(D shared), $(D inout), $(D const shared), $(D const inout), $(D inout shared), $(D const inout shared), $(D immutable)), $(TROW $(I mutable), $(YES ), $(YES ), $(NO ), $(NO ), $(NO ), $(NO ), $(NO ), $(NO ), $(NO ) ) $(TROW $(D const), $(NO ), $(YES ), $(NO ), $(NO ), $(NO ), $(NO ), $(NO ), $(NO ), $(NO ) ) $(TROW $(D shared), $(NO ), $(NO ), $(YES ), $(NO ), $(YES ), $(NO ), $(NO ), $(NO ), $(NO ) ) $(TROW $(D inout), $(NO ), $(YES ), $(NO ), $(YES ), $(NO ), $(YES ), $(NO ), $(NO ), $(NO ) ) $(TROW $(D const shared), $(NO ), $(NO ), $(NO ), $(NO ), $(YES ), $(NO ), $(NO ), $(NO ), $(NO ) ) $(TROW $(D const inout), $(NO ), $(YES ), $(NO ), $(NO ), $(NO ), $(YES ), $(NO ), $(NO ), $(NO ) ) $(TROW $(D inout shared), $(NO ), $(NO ), $(NO ), $(NO ), $(YES ), $(NO ), $(YES ), $(YES ), $(NO ) ) $(TROW $(D const inout shared), $(NO ), $(NO ), $(NO ), $(NO ), $(YES ), $(NO ), $(NO ), $(YES ), $(NO ) ) $(TROW $(D immutable), $(NO ), $(YES ), $(NO ), $(NO ), $(YES ), $(YES ), $(NO ), $(YES ), $(YES )) ) $(DDOC_BLANKLINE )

$(LNAME2 unique-expressions, Unique Expressions)

$(DDOC_BLANKLINE ) $(P If an implicit conversion is disallowed by the table, an $(GLINK2 expression, Expression) may be implicitly converted as follows: ) $(UL $(LI From mutable or shared to immutable if the expression is unique and all expressions it transitively refers to are either unique or immutable. ) $(LI From mutable to shared if the expression is unique and all expressions it transitively refers to are either unique, immutable, or shared. ) $(LI From immutable to mutable if the expression is unique. ) $(LI From shared to mutable if the expression is unique. ) ) $(DDOC_BLANKLINE ) $(P A $(I Unique Expression) is one for which there are no other references to the value of the expression and all expressions it transitively refers to are either also unique or are immutable. For example:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) main() { $(D_KEYWORD immutable) $(D_KEYWORD int)** p = $(D_KEYWORD new) $(D_KEYWORD int)*($(D_KEYWORD null)); $(D_COMMENT // ok, unique ) $(D_KEYWORD int) x; $(D_COMMENT //immutable int** q = new int*$(LPAREN)&x$(RPAREN ); // error, there may be other references to x ) $(D_KEYWORD immutable) $(D_KEYWORD int) y; $(D_KEYWORD immutable) $(D_KEYWORD int)** r = $(D_KEYWORD new) $(D_KEYWORD immutable)($(D_KEYWORD int))*(&y); $(D_COMMENT // ok, y is immutable )} ) ) $(P See also: $(DDSUBLINK spec/function, pure-factory-functions, Pure Factory Functions).) $(DDOC_BLANKLINE ) $(P Otherwise, a $(GLINK2 expression, CastExpression) can be used to force a conversion when an implicit version is disallowed, but this cannot be done in $(D @safe) code, and the correctness of it must be verified by the user. ) $(DDOC_BLANKLINE ) $(SPEC_SUBNAV_PREV_NEXT enum, Enums, function, Functions) ) )