union

A bare union defines a set of possible types that a value can be as a list of fields. Only one field can be active at a time. The in-memory representation of bare unions is not guaranteed. Bare unions cannot be used to reinterpret memory. For that, use @ptrCast, or use an extern union or a packed union which have guaranteed in-memory layout. Accessing the non-active field is safety-checked Undefined Behavior:

test.zig

  1. constPayload=union{
  2. Int: i64,
  3. Float: f64,
  4. Bool:bool,
  5. };
  6. test "simple union"{
  7. var payload =Payload{.Int=1234};
  8. payload.Float=12.34;
  9. }
  1. $ zig test test.zig
  2. 1/1 test "simple union"...access of inactive union field
  3. /deps/zig/docgen_tmp/test.zig:8:12:0x204c1bin test "simple union"(test)
  4. payload.Float=12.34;
  5. ^
  6. /deps/zig/lib/std/special/test_runner.zig:47:28:0x22bb1ein std.special.main (test)
  7. }else test_fn.func();
  8. ^
  9. /deps/zig/lib/std/start.zig:253:37:0x20564din std.start.posixCallMainAndExit (test)
  10. const result = root.main()catch|err|{
  11. ^
  12. /deps/zig/lib/std/start.zig:123:5:0x20538fin std.start._start (test)
  13. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  14. ^
  15. Tests failed.Use the following command to reproduce the failure:
  16. /deps/zig/docgen_tmp/test

You can activate another field by assigning the entire union:

test.zig

  1. const std =@import("std");
  2. constassert= std.debug.assert;
  3. constPayload=union{
  4. Int: i64,
  5. Float: f64,
  6. Bool:bool,
  7. };
  8. test "simple union"{
  9. var payload =Payload{.Int=1234};
  10. assert(payload.Int==1234);
  11. payload =Payload{.Float=12.34};
  12. assert(payload.Float==12.34);
  13. }
  1. $ zig test test.zig
  2. 1/1 test "simple union"...OK
  3. All1 tests passed.

In order to use switch with a union, it must be a Tagged union.

To initialize a union when the tag is a comptime-known name, see @unionInit.

Tagged union

Unions can be declared with an enum tag type. This turns the union into a tagged union, which makes it eligible to use with switch expressions. One can use @TagType to obtain the enum type from the union type. Tagged unions coerce to their tag type: Type Coercion: unions and enums.

test.zig

  1. const std =@import("std");
  2. constassert= std.debug.assert;
  3. constComplexTypeTag=enum{
  4. Ok,
  5. NotOk,
  6. };
  7. constComplexType=union(ComplexTypeTag){
  8. Ok: u8,
  9. NotOk:void,
  10. };
  11. test "switch on tagged union"{
  12. const c =ComplexType{.Ok=42};
  13. assert(@as(ComplexTypeTag, c)==ComplexTypeTag.Ok);
  14. switch(c){
  15. ComplexTypeTag.Ok=>|value|assert(value ==42),
  16. ComplexTypeTag.NotOk=> unreachable,
  17. }
  18. }
  19. test "@TagType"{
  20. assert(@TagType(ComplexType)==ComplexTypeTag);
  21. }
  22. test "coerce to enum"{
  23. const c1 =ComplexType{.Ok=42};
  24. const c2 =ComplexType.NotOk;
  25. assert(c1 ==.Ok);
  26. assert(c2 ==.NotOk);
  27. }
  1. $ zig test test.zig
  2. 1/3 test "switch on tagged union"...OK
  3. 2/3 test "@TagType"...OK
  4. 3/3 test "coerce to enum"...OK
  5. All3 tests passed.

In order to modify the payload of a tagged union in a switch expression, place a * before the variable name to make it a pointer:

test.zig

  1. const std =@import("std");
  2. constassert= std.debug.assert;
  3. constComplexTypeTag=enum{
  4. Ok,
  5. NotOk,
  6. };
  7. constComplexType=union(ComplexTypeTag){
  8. Ok: u8,
  9. NotOk:void,
  10. };
  11. test "modify tagged union in switch"{
  12. var c =ComplexType{.Ok=42};
  13. assert(@as(ComplexTypeTag, c)==ComplexTypeTag.Ok);
  14. switch(c){
  15. ComplexTypeTag.Ok=>|*value| value.*+=1,
  16. ComplexTypeTag.NotOk=> unreachable,
  17. }
  18. assert(c.Ok==43);
  19. }
  1. $ zig test test.zig
  2. 1/1 test "modify tagged union in switch"...OK
  3. All1 tests passed.

Unions can be made to infer the enum tag type. Further, unions can have methods just like structs and enums.

test.zig

  1. const std =@import("std");
  2. constassert= std.debug.assert;
  3. constVariant=union(enum){
  4. Int: i32,
  5. Bool:bool,
  6. // void can be omitted when inferring enum tag type.
  7. None,
  8. fn truthy(self:Variant)bool{
  9. returnswitch(self){
  10. Variant.Int=>|x_int| x_int !=0,
  11. Variant.Bool=>|x_bool| x_bool,
  12. Variant.None=>false,
  13. };
  14. }
  15. };
  16. test "union method"{
  17. var v1 =Variant{.Int=1};
  18. var v2 =Variant{.Bool=false};
  19. assert(v1.truthy());
  20. assert(!v2.truthy());
  21. }
  1. $ zig test test.zig
  2. 1/1 test "union method"...OK
  3. All1 tests passed.

@tagName can be used to return a comptime[]const u8 value representing the field name:

test.zig

  1. const std =@import("std");
  2. constassert= std.debug.assert;
  3. constSmall2=union(enum){
  4. A: i32,
  5. B:bool,
  6. C: u8,
  7. };
  8. test "@tagName"{
  9. assert(std.mem.eql(u8,@tagName(Small2.C),"C"));
  10. }
  1. $ zig test test.zig
  2. 1/1 test "@tagName"...OK
  3. All1 tests passed.

extern union

An extern union has memory layout guaranteed to be compatible with the target C ABI.

See also:

packed union

A packed union has well-defined in-memory layout and is eligible to be in a packed struct.

Anonymous Union Literals

Anonymous Struct Literals syntax can be used to initialize unions without specifying the type:

anon_union.zig

  1. const std =@import("std");
  2. constassert= std.debug.assert;
  3. constNumber=union{
  4. int: i32,
  5. float: f64,
  6. };
  7. test "anonymous union literal syntax"{
  8. var i:Number=.{.int=42};
  9. var f = makeNumber();
  10. assert(i.int==42);
  11. assert(f.float==12.34);
  12. }
  13. fn makeNumber()Number{
  14. return.{.float=12.34};
  15. }
  1. $ zig test anon_union.zig
  2. 1/1 test "anonymous union literal syntax"...OK
  3. All1 tests passed.