$(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, ,
)
$(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, ,
)
$(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)
)
)