Commit 22dd0db9bf
Changed files (1)
doc/langref.html.in
@@ -2046,6 +2046,13 @@ test "linked list" {
assert(list2.first.?.data == 1234);
}
{#code_end#}
+
+ {#header_open|extern struct#}
+ <p>An {#syntax#}extern struct{#endsyntax#} has in-memory layout guaranteed to match the
+ C ABI for the target.</p>
+ {#see_also|extern union|extern enum#}
+ {#header_close#}
+
{#header_open|packed struct#}
<p>
Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout:
@@ -2412,12 +2419,32 @@ test "packed enum" {
{#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
{#header_close#}
{#header_open|union#}
- {#code_begin|test|union#}
+ <p>
+ A bare {#syntax#}union{#endsyntax#} 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 {#link|@ptrCast#},
+ or use an {#link|extern union#} or a {#link|packed union#} which have
+ guaranteed in-memory layout.
+ {#link|Accessing the non-active field|Wrong Union Field Access#} is
+ safety-checked {#link|Undefined Behavior#}:
+ </p>
+ {#code_begin|test_err|inactive union field#}
+const Payload = union {
+ Int: i64,
+ Float: f64,
+ Bool: bool,
+};
+test "simple union" {
+ var payload = Payload{ .Int = 1234 };
+ payload.Float = 12.34;
+}
+ {#code_end#}
+ <p>You can activate another field by assigning the entire union:</p>
+ {#code_begin|test#}
const std = @import("std");
const assert = std.debug.assert;
-const mem = std.mem;
-// A union has only 1 active field at a time.
const Payload = union {
Int: i64,
Float: f64,
@@ -2425,14 +2452,25 @@ const Payload = union {
};
test "simple union" {
var payload = Payload{ .Int = 1234 };
- // payload.Float = 12.34; // ERROR! field not active
assert(payload.Int == 1234);
- // You can activate another field by assigning the entire union.
payload = Payload{ .Float = 12.34 };
assert(payload.Float == 12.34);
}
+ {#code_end#}
+ <p>
+ In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}.
+ </p>
+
+ {#header_open|Tagged union#}
+ <p>Unions can be declared with an enum tag type.
+ This turns the union into a <em>tagged</em> union, which makes it eligible
+ to use with {#link|switch#} expressions. One can use {#link|@TagType#} to
+ obtain the enum type from the union type.
+ </p>
+ {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
-// Unions can be given an enum tag type:
const ComplexTypeTag = enum {
Ok,
NotOk,
@@ -2442,56 +2480,68 @@ const ComplexType = union(ComplexTypeTag) {
NotOk: void,
};
-// Declare a specific instance of the union variant.
-test "declare union value" {
- const c = ComplexType{ .Ok = 0 };
+test "switch on tagged union" {
+ const c = ComplexType{ .Ok = 42 };
assert(ComplexTypeTag(c) == ComplexTypeTag.Ok);
+
+ switch (c) {
+ ComplexTypeTag.Ok => |value| assert(value == 42),
+ ComplexTypeTag.NotOk => unreachable,
+ }
}
-// @TagType can be used to access the enum tag type of a tagged union.
test "@TagType" {
assert(@TagType(ComplexType) == ComplexTypeTag);
}
+ {#code_end#}
+ <p>In order to modify the payload of a tagged union in a switch expression,
+ place a {#syntax#}*{#endsyntax#} before the variable name to make it a pointer:
+ </p>
+ {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
-// Unions can be made to infer the enum tag type.
-const Foo = union(enum) {
- String: []const u8,
- Number: u64,
-
- // void can be omitted when inferring enum tag type.
- None,
+const ComplexTypeTag = enum {
+ Ok,
+ NotOk,
+};
+const ComplexType = union(ComplexTypeTag) {
+ Ok: u8,
+ NotOk: void,
};
-test "union variant switch" {
- const p = Foo{ .Number = 54 };
- const what_is_it = switch (p) {
- // Capture by reference
- Foo.String => |*x| blk: {
- break :blk "this is a string";
- },
- // Capture by value
- Foo.Number => |x| blk: {
- assert(x == 54);
- break :blk "this is a number";
- },
+test "modify tagged union in switch" {
+ var c = ComplexType{ .Ok = 42 };
+ assert(ComplexTypeTag(c) == ComplexTypeTag.Ok);
- Foo.None => blk: {
- break :blk "this is a none";
- },
- };
- assert(mem.eql(u8, what_is_it, "this is a number"));
-}
+ switch (c) {
+ ComplexTypeTag.Ok => |*value| value.* += 1,
+ ComplexTypeTag.NotOk => unreachable,
+ }
-// Unions can have methods just like structs and enums:
+ assert(c.Ok == 43);
+}
+ {#code_end#}
+ <p>
+ Unions can be made to infer the enum tag type.
+ Further, unions can have methods just like structs and enums.
+ </p>
+ {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
const Variant = union(enum) {
Int: i32,
Bool: bool,
+ // void can be omitted when inferring enum tag type.
+ None,
+
fn truthy(self: Variant) bool {
return switch (self) {
Variant.Int => |x_int| x_int != 0,
Variant.Bool => |x_bool| x_bool,
+ Variant.None => false,
};
}
};
@@ -2503,38 +2553,34 @@ test "union method" {
assert(v1.truthy());
assert(!v2.truthy());
}
+ {#code_end#}
+ <p>
+ {#link|@tagName#} can be used to return a {#link|comptime#}
+ {#syntax#}[]const u8{#endsyntax#} value representing the field name:
+ </p>
+ {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
-const Small = union {
- A: i32,
- B: bool,
- C: u8,
-};
-
-// @memberCount tells how many fields a union has:
-test "@memberCount" {
- assert(@memberCount(Small) == 3);
-}
-
-// @memberName tells the name of a field in an enum:
-test "@memberName" {
- assert(mem.eql(u8, @memberName(Small, 1), "B"));
-}
-
-// @tagName gives a []const u8 representation of an enum value,
-// but only if the union has an enum tag type.
const Small2 = union(enum) {
A: i32,
B: bool,
C: u8,
};
test "@tagName" {
- assert(mem.eql(u8, @tagName(Small2.C), "C"));
+ assert(std.mem.eql(u8, @tagName(Small2.C), "C"));
}
{#code_end#}
+ {#header_close#}
+
+ {#header_open|extern union#}
<p>
- Unions with an enum tag are generated as a struct with a tag field and union field. Zig
- sorts the order of the tag and union field by the largest alignment.
+ An {#syntax#}extern union{#endsyntax#} has memory layout guaranteed to be compatible with
+ the target C ABI.
</p>
+ {#see_also|extern struct#}
+ {#header_close#}
+
{#header_open|packed union#}
<p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
to be in a {#link|packed struct#}.
@@ -2623,7 +2669,7 @@ test "switch simple" {
// Ranges can be specified using the ... syntax. These are inclusive
// both ends.
- 5 ... 100 => 1,
+ 5...100 => 1,
// Branches can be arbitrarily complex.
101 => blk: {
@@ -2649,14 +2695,47 @@ test "switch simple" {
assert(b == 1);
}
-test "switch enum" {
+// Switch expressions can be used outside a function:
+const os_msg = switch (builtin.os) {
+ builtin.Os.linux => "we found a linux user",
+ else => "not a linux user",
+};
+
+// Inside a function, switch statements implicitly are compile-time
+// evaluated if the target expression is compile-time known.
+test "switch inside function" {
+ switch (builtin.os) {
+ builtin.Os.fuchsia => {
+ // On an OS other than fuchsia, block is not even analyzed,
+ // so this compile error is not triggered.
+ // On fuchsia this compile error would be triggered.
+ @compileError("fuchsia not supported");
+ },
+ else => {},
+ }
+}
+ {#code_end#}
+ <p>
+ {#syntax#}switch{#endsyntax#} can be used to capture the field values
+ of a {#link|Tagged union#}. Modifications to the field values can be
+ done by placing a {#syntax#}*{#endsyntax#} before the capture variable name,
+ turning it into a pointer.
+ </p>
+ {#code_begin|test#}
+const assert = @import("std").debug.assert;
+
+test "switch on tagged union" {
+ const Point = struct {
+ x: u8,
+ y: u8,
+ };
const Item = union(enum) {
A: u32,
- C: struct { x: u8, y: u8 },
+ C: Point,
D,
};
- var a = Item { .A = 3 };
+ var a = Item{ .C = Point{ .x = 1, .y = 2 } };
// Switching on more complex enums is allowed.
const b = switch (a) {
@@ -2674,27 +2753,8 @@ test "switch enum" {
Item.D => 8,
};
- assert(b == 3);
-}
-
-// Switch expressions can be used outside a function:
-const os_msg = switch (builtin.os) {
- builtin.Os.linux => "we found a linux user",
- else => "not a linux user",
-};
-
-// Inside a function, switch statements implicitly are compile-time
-// evaluated if the target expression is compile-time known.
-test "switch inside function" {
- switch (builtin.os) {
- builtin.Os.fuchsia => {
- // On an OS other than fuchsia, block is not even analyzed,
- // so this compile error is not triggered.
- // On fuchsia this compile error would be triggered.
- @compileError("fuchsia not supported");
- },
- else => {},
- }
+ assert(b == 6);
+ assert(a.C.x == 2);
}
{#code_end#}
{#see_also|comptime|enum|@compileError|Compile Variables#}
@@ -7630,6 +7690,7 @@ fn bar(f: *Foo) void {
f.float = 12.34;
}
{#code_end#}
+ {#see_also|union|extern union#}
{#header_close#}
{#header_open|Out of Bounds Float to Integer Cast#}