Skip to content

Commit

Permalink
Document difference between type constructors & storage classes.
Browse files Browse the repository at this point in the history
  • Loading branch information
H. S. Teoh committed Nov 14, 2014
1 parent 26f94d5 commit 6d194e1
Showing 1 changed file with 122 additions and 0 deletions.
122 changes: 122 additions & 0 deletions declaration.dd
Expand Up @@ -606,8 +606,130 @@ $(H3 Global and Static Initializers)
functions or data is implementation defined.
Runtime initialization can be done with static constructors.
)

$(H3 Type Constructors vs. Storage Classes)

$(P D draws a distinction between a type constructor and a storage
class.)

$(P A $(I type constructor) creates a derived type from an existing
base type, and the resulting type may be used to create multiple
instances of that type.)

$(P For example, the $(D immutable) type constructor can be used to
create variables of immutable type, such as:)

--------
immutable(int) x; // typeof(x) == immutable(int)
immutable(int)[] y; // typeof(y) == immutable(int)[]
// typeof(y[0]) == immutable(int)

// Type constructors create new types that can be aliased:
alias ImmutableInt = immutable(int);
ImmutableInt z; // typeof(z) == immutable(int)
--------

$(P A $(I storage class), on the other hand, does not create a new
type, but describes only the type of storage used by the variable or
function being declared. For example, a member function can be declared
with the $(D const) storage class to indicate that it does not modify
its implicit $(D this) argument:)

--------
struct S
{
int x;
int method() const
{
//x++; // Error: this method is const and cannot modify this.x
return x; // OK: we can still read this.x
}
}
--------

$(P Although some keywords can be used both as a type constructor and a
storage class, there are some storage classes that cannot be used to
construct new types. One example is $(D ref):)

--------
// ref declares the parameter x to be passed by reference
void func(ref int x)
{
x++; // so modifications to x will be visible in the caller
}

void main()
{
auto x = 1;
func(x);
assert(x == 2);

// However, ref is not a type constructor, so the following is illegal:
ref(int) y; // Error: ref is not a type constructor.
}
--------

--------
// Functions can also be declared as 'ref', meaning their return value is
// passed by reference:
ref int func2()
{
static int y = 0;
return y;
}

void main()
{
func2() = 2; // The return value of func2() can be modified.
assert(func2() == 2);

// However, the reference returned by func2() does not propagate to
// variables, because the 'ref' only applies to the return value itself,
// not to any subsequent variable created from it:
auto x = func2();
static assert(typeof(x) == int); // N.B.: *not* ref(int);
// there is no such type as ref(int).
x++;
assert(x == 3);
assert(func2() == 2); // x is not a reference to what func2() returned; it
// does not inherit the ref storage class from func2().
}
--------

$(P Due to the fact that some keywords, such as $(D const), can be used
both as a type constructor and a storage class, it may sometimes result
in ambiguous-looking code:)

--------
struct S
{
// Is const here a type constructor or a storage class?
// Is the return value const(int), or is this a const function that returns
// (mutable) int?
const int func() { return 1; }
}
--------

$(P To avoid such confusion, it is recommended that type constructor
syntax with parentheses always be used for return types, and that
function storage classes be written on the right-hand side of the
declaration instead of the left-hand side where it may be visually
confused with the return type:)

--------
struct S
{
// Now it is clear that the 'const' here applies to the return type:
const(int) func1() { return 1; }

// And it is clear that the 'const' here applies to the function:
int func2() const { return 1; }
}
--------

)


Macros:
TITLE=Declarations
WIKI=Declaration
Expand Down

0 comments on commit 6d194e1

Please sign in to comment.