Im­prove this page Quickly fork, edit on­line, and sub­mit a pull re­quest for this page. Re­quires a signed-in GitHub ac­count. This works well for small changes. If you'd like to make larger changes you may want to con­sider using local clone. Page wiki View or edit the com­mu­nity-main­tained wiki page as­so­ci­ated with this page.

Structs, Unions

Whereas classes are ref­er­ence types, structs are value types. Any C struct can be ex­actly rep­re­sented as a D struct, ex­cept non-sta­tic func­tion-nested D structs which ac­cess the con­text of their en­clos­ing scope. Structs and unions are meant as sim­ple ag­gre­ga­tions of data, or as a way to paint a data struc­ture over hard­ware or an ex­ter­nal type. Ex­ter­nal types can be de­fined by the op­er­at­ing sys­tem API, or by a file for­mat. Ob­ject ori­ented fea­tures are pro­vided with the class data type.

A struct is de­fined to not have an iden­tity; that is, the im­ple­men­ta­tion is free to make bit copies of the struct as con­ve­nient.

Struct, Class Com­par­i­son Table
FeaturestructclassC structC++ structC++ class
value type
ref­er­ence type
data mem­bers
hid­den mem­bers
sta­tic mem­bers
de­fault mem­ber ini­tial­iz­ers
bit fields
non-vir­tual mem­ber func­tions
vir­tual mem­ber func­tions
con­struc­tors
post­blit/copy con­struc­tors
de­struc­tors
Shared­Sta­t­ic­Construc­tors
Shared­Sta­t­icDe­struc­tors
RAII
as­sign over­load
lit­er­als
op­er­a­tor over­load­ing
in­her­i­tance
in­vari­ants
unit tests
syn­chro­niz­able
pa­ra­me­ter­i­z­able
align­ment con­trol
mem­ber pro­tec­tion
de­fault pub­lic
tag name space
anony­mous
sta­tic con­struc­tor
sta­tic de­struc­tor
const/im­mutable/shared
inner nest­ing
AggregateDeclaration:
    struct Identifier StructBody
    union Identifier StructBody
    struct Identifier ;
    union Identifier ;
    StructTemplateDeclaration
    UnionTemplateDeclaration

StructBody:
    { }
    { StructBodyDeclarations }

StructBodyDeclarations:
    StructBodyDeclaration
    StructBodyDeclaration StructBodyDeclarations

StructBodyDeclaration:
    DeclDef
    StructAllocator
    StructDeallocator
    StructPostblit
    AliasThis

StructAllocator:
    ClassAllocator

StructDeallocator:
    ClassDeallocator

They work like they do in C, with the fol­low­ing ex­cep­tions:

Opaque Structs and Unions

Opaque struct and union de­c­la­ra­tions do not have a Struct­Body:

struct S;
union U;

The mem­bers are com­pletely hid­den to the user, and so the only op­er­a­tions on those types are ones that do not re­quire any knowl­edge of the con­tents of those types. For ex­am­ple:

struct S;
S.sizeof; // error, size is not known
S s;      // error, cannot initialize unknown contents
S* p;     // ok, knowledge of members is not necessary

They can be used to im­ple­ment the PIMPL idiom.

Sta­tic Ini­tial­iza­tion of Structs

Sta­tic struct mem­bers are by de­fault ini­tial­ized to what­ever the de­fault ini­tial­izer for the mem­ber is, and if none sup­plied, to the de­fault ini­tial­izer for the mem­ber's type. If a sta­tic ini­tial­izer is sup­plied, the mem­bers are ini­tial­ized by the mem­ber name, colon, ex­pres­sion syn­tax. The mem­bers may be ini­tial­ized in any order. Ini­tial­iz­ers for sta­t­ics must be eval­u­at­able at com­pile time. Mem­bers not spec­i­fied in the ini­tial­izer list are de­fault ini­tial­ized.
struct S { int a; int b; int c; int d = 7;}
static S x = { a:1, b:2};            // c is set to 0, d to 7
static S z = { c:4, b:5, a:2 , d:5}; // z.a = 2, z.b = 5, z.c = 4, z.d = 5
C-style ini­tial­iza­tion, based on the order of the mem­bers in the struct de­c­la­ra­tion, is also sup­ported:
static S q = { 1, 2 }; // q.a = 1, q.b = 2, q.c = 0, q.d = 7

Struct lit­er­als can also be used to ini­tial­ize sta­t­ics, but they must be evalu­able at com­pile time.

static S q = S( 1, 2+3 ); // q.a = 1, q.b = 5, q.c = 0, q.d = 7

The sta­tic ini­tial­izer syn­tax can also be used to ini­tial­ize non-sta­tic vari­ables, pro­vided that the mem­ber names are not given. The ini­tial­izer need not be eval­u­at­able at com­pile time.

void test(int i) {
  S q = { 1, i }; // q.a = 1, q.b = i, q.c = 0, q.d = 7
}

Sta­tic Ini­tial­iza­tion of Unions

Unions are ini­tial­ized ex­plic­itly.
union U { int a; double b; }
static U u = { b : 5.0 }; // u.b = 5.0
Other mem­bers of the union that over­lay the ini­tial­izer, but oc­cupy more stor­age, have the extra stor­age ini­tial­ized to zero.

Dy­namic Ini­tial­iza­tion of Structs

Structs can be dy­nam­i­cally ini­tial­ized from an­other value of the same type:

struct S { int a; }
S t;      // default initialized
t.a = 3;
S s = t;  // s.a is set to 3

If opCall is over­rid­den for the struct, and the struct is ini­tial­ized with a value that is of a dif­fer­ent type, then the opCall op­er­a­tor is called:

struct S {
  int a;

  static S opCall(int v)
  { S s;
    s.a = v;
    return s;
  }

  static S opCall(S v)
  { S s;
    s.a = v.a + 1;
    return s;
  }
}

S s = 3; // sets s.a to 3
S t = s; // sets t.a to 3, S.opCall(s) is not called

Struct Lit­er­als

Struct lit­er­als con­sist of the name of the struct fol­lowed by a paren­the­sized ar­gu­ment list:

struct S { int x; float y; }

int foo(S s) { return s.x; }

foo( S(1, 2) ); // set field x to 1, field y to 2

Struct lit­er­als are syn­tac­ti­cally like func­tion calls. If a struct has a mem­ber func­tion named opCall, then struct lit­er­als for that struct are not pos­si­ble. It is an error if there are more ar­gu­ments than fields of the struct. If there are fewer ar­gu­ments than fields, the re­main­ing fields are ini­tial­ized with their re­spec­tive de­fault ini­tial­iz­ers. If there are anony­mous unions in the struct, only the first mem­ber of the anony­mous union can be ini­tial­ized with a struct lit­eral, and all sub­se­quent non-over­lap­ping fields are de­fault ini­tial­ized.

Struct Prop­er­ties

Struct Prop­er­ties
NameDescription
.sizeofSize in bytes of struct
.alignofSize bound­ary struct needs to be aligned on
.tupleofGets type tuple of fields

Struct Field Prop­er­ties

Struct Field Prop­er­ties
NameDescription
.offsetofOff­set in bytes of field from be­gin­ning of struct

Const and Im­mutable Structs

A struct de­c­la­ra­tion can have a stor­age class of const, immutable or shared. It has an equiv­a­lent ef­fect as de­clar­ing each mem­ber of the struct as const, immutable or shared.

const struct S { int a; int b = 2; }

void main() {
  S s = S(3); // initializes s.a to 3
  S t;        // initializes t.a to 0
  t = s;      // error, t is const
  t.a = 4;    // error, t.a is const
}

Struct Con­struc­tors

Struct con­struc­tors are used to ini­tial­ize an in­stance of a struct. The Pa­ra­me­terList may not be empty. Struct in­stances that are not in­stan­ti­ated with a con­struc­tor are de­fault ini­tial­ized to their .init value.

struct S {
  int x, y;

  this()  // error, cannot implement default ctor for structs
  {
  }

  this(int a, int b)
  {
    x = a;
    y = b;
  }
}

void main()
{
  S a = S(4, 5);
  auto b = S();  // same as auto b = S.init;
}

Struct Post­blits

StructPostblit:
    this(this) FunctionBody

Copy con­struc­tion is de­fined as ini­tial­iz­ing a struct in­stance from an­other struct of the same type. Copy con­struc­tion is di­vided into two parts:

  1. blit­ting the fields, i.e. copy­ing the bits
  2. run­ning post­blit on the re­sult

The first part is done au­to­mat­i­cally by the lan­guage, the sec­ond part is done if a post­blit func­tion is de­fined for the struct. The post­blit has ac­cess only to the des­ti­na­tion struct ob­ject, not the source. Its job is to ‘fix up’ the des­ti­na­tion as nec­es­sary, such as mak­ing copies of ref­er­enced data, in­cre­ment­ing ref­er­ence counts, etc. For ex­am­ple:

struct S {
  int[] a;    // array is privately owned by this instance
  this(this) {
    a = a.dup;
  }
  ~this() {
    delete a;
  }
}

Unions may not have fields that have post­blits.

Struct De­struc­tors

De­struc­tors are called when an ob­ject goes out of scope. Their pur­pose is to free up re­sources owned by the struct ob­ject.

Unions may not have fields that have de­struc­tors.

As­sign­ment Over­load

While copy con­struc­tion takes care of ini­tial­iz­ing an ob­ject from an­other ob­ject of the same type, as­sign­ment is de­fined as copy­ing the con­tents of one ob­ject over an­other, al­ready ini­tial­ized, type:

struct S { ... }
S s;      // default construction of s
S t = s;  // t is copy-constructed from s
t = s;    // t is assigned from s

Struct as­sign­ment t=s is de­fined to be se­man­ti­cally equiv­a­lent to:

t.opAssign(s);

where opAssign is a mem­ber func­tion of S:

S* opAssign(ref const S s)
{   ... bitcopy *this into tmp ...
    ... bitcopy s into *this ...
    ... call destructor on tmp ...
    return this;
}

While the com­piler will gen­er­ate a de­fault opAssign as needed, a user-de­fined one can be sup­plied. The user-de­fined one must still im­ple­ment the equiv­a­lent se­man­tics, but can be more ef­fi­cient.

One rea­son a cus­tom opAssign might be more ef­fi­cient is if the struct has a ref­er­ence to a local buffer:

struct S {
  int[] buf;
  int a;

  S* opAssign(ref const S s) {
    a = s.a;
    return this;
  }

  this(this) {
    buf = buf.dup;
  }

  ~this() {
    delete buf;
  }
}

Here, S has a tem­po­rary work­space buf[]. The nor­mal post­blit will point­lessly free and re­al­lo­cate it. The cus­tom opAssign will reuse the ex­ist­ing stor­age.

Nested Structs

A nested struct is a struct that is de­clared in­side the scope of a func­tion or a tem­plated struct that has aliases to local func­tions as a tem­plate ar­gu­ment. Nested structs have mem­ber func­tions. It has ac­cess to the con­text of its en­clos­ing scope (via an added hid­den field).

void foo() {
  int i = 7;
  struct SS {
    int x,y;
    int bar() { return x + i + 1; }
  }
  SS s;
  s.x = 3;
  s.bar(); // returns 11
}

A struct can be pre­vented from being nested by using the sta­tic at­tribute, but then of course it will not be able to ac­cess vari­ables from its en­clos­ing scope.

void foo() {
  int i = 7;
  static struct SS {
    int x,y;
    int bar() {
      return i; // error, SS is not a nested struct
    }
  }
}

A tem­plated struct can be­come a nested struct if it has a local func­tion passed as an aliased ar­gu­ment:

struct A(alias F) {
  int fun(int i) { return F(i); }
}

A!(F) makeA(alias F)() {return A!(F)(); }

void main() {
  int x = 40;
  int fun(int i) { return x + i; }
  A!(fun) a = makeA!(fun)();
  a.fun(2);
}
Forums | Comments | Search | Downloads | Home