authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-26 22:46:35-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-26 22:57:01-05:00
log22dd0db9bf18e225799e8540ec27451fc48ea86e
tree0c0e93a75a4c6391d1a9eefdd2fd14cba9503bde
parent0d48011f5eb15d02b7caf9f5f01c3eecb5c4754f
signaturelock-open Commit is signed but in an unrecognized format.

improve docs for unions and switching on tagged unions

closes #1943

1 files changed, 143 insertions(+), 82 deletions(-)

doc/langref.html.in+143-82
......@@ -2046,6 +2046,13 @@ test "linked list" {
20462046 assert(list2.first.?.data == 1234);
20472047}
20482048 {#code_end#}
2049
2050 {#header_open|extern struct#}
2051 <p>An {#syntax#}extern struct{#endsyntax#} has in-memory layout guaranteed to match the
2052 C ABI for the target.</p>
2053 {#see_also|extern union|extern enum#}
2054 {#header_close#}
2055
20492056 {#header_open|packed struct#}
20502057 <p>
20512058 Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout:
......@@ -2412,12 +2419,32 @@ test "packed enum" {
24122419 {#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
24132420 {#header_close#}
24142421 {#header_open|union#}
2415 {#code_begin|test|union#}
2422 <p>
2423 A bare {#syntax#}union{#endsyntax#} defines a set of possible types that a value
2424 can be as a list of fields. Only one field can be active at a time.
2425 The in-memory representation of bare unions is not guaranteed.
2426 Bare unions cannot be used to reinterpret memory. For that, use {#link|@ptrCast#},
2427 or use an {#link|extern union#} or a {#link|packed union#} which have
2428 guaranteed in-memory layout.
2429 {#link|Accessing the non-active field|Wrong Union Field Access#} is
2430 safety-checked {#link|Undefined Behavior#}:
2431 </p>
2432 {#code_begin|test_err|inactive union field#}
2433const Payload = union {
2434 Int: i64,
2435 Float: f64,
2436 Bool: bool,
2437};
2438test "simple union" {
2439 var payload = Payload{ .Int = 1234 };
2440 payload.Float = 12.34;
2441}
2442 {#code_end#}
2443 <p>You can activate another field by assigning the entire union:</p>
2444 {#code_begin|test#}
24162445const std = @import("std");
24172446const assert = std.debug.assert;
2418const mem = std.mem;
24192447
2420// A union has only 1 active field at a time.
24212448const Payload = union {
24222449 Int: i64,
24232450 Float: f64,
......@@ -2425,14 +2452,25 @@ const Payload = union {
24252452};
24262453test "simple union" {
24272454 var payload = Payload{ .Int = 1234 };
2428 // payload.Float = 12.34; // ERROR! field not active
24292455 assert(payload.Int == 1234);
2430 // You can activate another field by assigning the entire union.
24312456 payload = Payload{ .Float = 12.34 };
24322457 assert(payload.Float == 12.34);
24332458}
2459 {#code_end#}
2460 <p>
2461 In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}.
2462 </p>
2463
2464 {#header_open|Tagged union#}
2465 <p>Unions can be declared with an enum tag type.
2466 This turns the union into a <em>tagged</em> union, which makes it eligible
2467 to use with {#link|switch#} expressions. One can use {#link|@TagType#} to
2468 obtain the enum type from the union type.
2469 </p>
2470 {#code_begin|test#}
2471const std = @import("std");
2472const assert = std.debug.assert;
24342473
2435// Unions can be given an enum tag type:
24362474const ComplexTypeTag = enum {
24372475 Ok,
24382476 NotOk,
......@@ -2442,56 +2480,68 @@ const ComplexType = union(ComplexTypeTag) {
24422480 NotOk: void,
24432481};
24442482
2445// Declare a specific instance of the union variant.
2446test "declare union value" {
2447 const c = ComplexType{ .Ok = 0 };
2483test "switch on tagged union" {
2484 const c = ComplexType{ .Ok = 42 };
24482485 assert(ComplexTypeTag(c) == ComplexTypeTag.Ok);
2486
2487 switch (c) {
2488 ComplexTypeTag.Ok => |value| assert(value == 42),
2489 ComplexTypeTag.NotOk => unreachable,
2490 }
24492491}
24502492
2451// @TagType can be used to access the enum tag type of a tagged union.
24522493test "@TagType" {
24532494 assert(@TagType(ComplexType) == ComplexTypeTag);
24542495}
2496 {#code_end#}
2497 <p>In order to modify the payload of a tagged union in a switch expression,
2498 place a {#syntax#}*{#endsyntax#} before the variable name to make it a pointer:
2499 </p>
2500 {#code_begin|test#}
2501const std = @import("std");
2502const assert = std.debug.assert;
24552503
2456// Unions can be made to infer the enum tag type.
2457const Foo = union(enum) {
2458 String: []const u8,
2459 Number: u64,
2460
2461 // void can be omitted when inferring enum tag type.
2462 None,
2504const ComplexTypeTag = enum {
2505 Ok,
2506 NotOk,
2507};
2508const ComplexType = union(ComplexTypeTag) {
2509 Ok: u8,
2510 NotOk: void,
24632511};
2464test "union variant switch" {
2465 const p = Foo{ .Number = 54 };
2466 const what_is_it = switch (p) {
2467 // Capture by reference
2468 Foo.String => |*x| blk: {
2469 break :blk "this is a string";
2470 },
24712512
2472 // Capture by value
2473 Foo.Number => |x| blk: {
2474 assert(x == 54);
2475 break :blk "this is a number";
2476 },
2513test "modify tagged union in switch" {
2514 var c = ComplexType{ .Ok = 42 };
2515 assert(ComplexTypeTag(c) == ComplexTypeTag.Ok);
24772516
2478 Foo.None => blk: {
2479 break :blk "this is a none";
2480 },
2481 };
2482 assert(mem.eql(u8, what_is_it, "this is a number"));
2483}
2517 switch (c) {
2518 ComplexTypeTag.Ok => |*value| value.* += 1,
2519 ComplexTypeTag.NotOk => unreachable,
2520 }
24842521
2485// Unions can have methods just like structs and enums:
2522 assert(c.Ok == 43);
2523}
2524 {#code_end#}
2525 <p>
2526 Unions can be made to infer the enum tag type.
2527 Further, unions can have methods just like structs and enums.
2528 </p>
2529 {#code_begin|test#}
2530const std = @import("std");
2531const assert = std.debug.assert;
24862532
24872533const Variant = union(enum) {
24882534 Int: i32,
24892535 Bool: bool,
24902536
2537 // void can be omitted when inferring enum tag type.
2538 None,
2539
24912540 fn truthy(self: Variant) bool {
24922541 return switch (self) {
24932542 Variant.Int => |x_int| x_int != 0,
24942543 Variant.Bool => |x_bool| x_bool,
2544 Variant.None => false,
24952545 };
24962546 }
24972547};
......@@ -2503,38 +2553,34 @@ test "union method" {
25032553 assert(v1.truthy());
25042554 assert(!v2.truthy());
25052555}
2556 {#code_end#}
2557 <p>
2558 {#link|@tagName#} can be used to return a {#link|comptime#}
2559 {#syntax#}[]const u8{#endsyntax#} value representing the field name:
2560 </p>
2561 {#code_begin|test#}
2562const std = @import("std");
2563const assert = std.debug.assert;
25062564
2507const Small = union {
2508 A: i32,
2509 B: bool,
2510 C: u8,
2511};
2512
2513// @memberCount tells how many fields a union has:
2514test "@memberCount" {
2515 assert(@memberCount(Small) == 3);
2516}
2517
2518// @memberName tells the name of a field in an enum:
2519test "@memberName" {
2520 assert(mem.eql(u8, @memberName(Small, 1), "B"));
2521}
2522
2523// @tagName gives a []const u8 representation of an enum value,
2524// but only if the union has an enum tag type.
25252565const Small2 = union(enum) {
25262566 A: i32,
25272567 B: bool,
25282568 C: u8,
25292569};
25302570test "@tagName" {
2531 assert(mem.eql(u8, @tagName(Small2.C), "C"));
2571 assert(std.mem.eql(u8, @tagName(Small2.C), "C"));
25322572}
25332573 {#code_end#}
2574 {#header_close#}
2575
2576 {#header_open|extern union#}
25342577 <p>
2535 Unions with an enum tag are generated as a struct with a tag field and union field. Zig
2536 sorts the order of the tag and union field by the largest alignment.
2578 An {#syntax#}extern union{#endsyntax#} has memory layout guaranteed to be compatible with
2579 the target C ABI.
25372580 </p>
2581 {#see_also|extern struct#}
2582 {#header_close#}
2583
25382584 {#header_open|packed union#}
25392585 <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
25402586 to be in a {#link|packed struct#}.
......@@ -2623,7 +2669,7 @@ test "switch simple" {
26232669
26242670 // Ranges can be specified using the ... syntax. These are inclusive
26252671 // both ends.
2626 5 ... 100 => 1,
2672 5...100 => 1,
26272673
26282674 // Branches can be arbitrarily complex.
26292675 101 => blk: {
......@@ -2649,14 +2695,47 @@ test "switch simple" {
26492695 assert(b == 1);
26502696}
26512697
2652test "switch enum" {
2698// Switch expressions can be used outside a function:
2699const os_msg = switch (builtin.os) {
2700 builtin.Os.linux => "we found a linux user",
2701 else => "not a linux user",
2702};
2703
2704// Inside a function, switch statements implicitly are compile-time
2705// evaluated if the target expression is compile-time known.
2706test "switch inside function" {
2707 switch (builtin.os) {
2708 builtin.Os.fuchsia => {
2709 // On an OS other than fuchsia, block is not even analyzed,
2710 // so this compile error is not triggered.
2711 // On fuchsia this compile error would be triggered.
2712 @compileError("fuchsia not supported");
2713 },
2714 else => {},
2715 }
2716}
2717 {#code_end#}
2718 <p>
2719 {#syntax#}switch{#endsyntax#} can be used to capture the field values
2720 of a {#link|Tagged union#}. Modifications to the field values can be
2721 done by placing a {#syntax#}*{#endsyntax#} before the capture variable name,
2722 turning it into a pointer.
2723 </p>
2724 {#code_begin|test#}
2725const assert = @import("std").debug.assert;
2726
2727test "switch on tagged union" {
2728 const Point = struct {
2729 x: u8,
2730 y: u8,
2731 };
26532732 const Item = union(enum) {
26542733 A: u32,
2655 C: struct { x: u8, y: u8 },
2734 C: Point,
26562735 D,
26572736 };
26582737
2659 var a = Item { .A = 3 };
2738 var a = Item{ .C = Point{ .x = 1, .y = 2 } };
26602739
26612740 // Switching on more complex enums is allowed.
26622741 const b = switch (a) {
......@@ -2674,27 +2753,8 @@ test "switch enum" {
26742753 Item.D => 8,
26752754 };
26762755
2677 assert(b == 3);
2678}
2679
2680// Switch expressions can be used outside a function:
2681const os_msg = switch (builtin.os) {
2682 builtin.Os.linux => "we found a linux user",
2683 else => "not a linux user",
2684};
2685
2686// Inside a function, switch statements implicitly are compile-time
2687// evaluated if the target expression is compile-time known.
2688test "switch inside function" {
2689 switch (builtin.os) {
2690 builtin.Os.fuchsia => {
2691 // On an OS other than fuchsia, block is not even analyzed,
2692 // so this compile error is not triggered.
2693 // On fuchsia this compile error would be triggered.
2694 @compileError("fuchsia not supported");
2695 },
2696 else => {},
2697 }
2756 assert(b == 6);
2757 assert(a.C.x == 2);
26982758}
26992759 {#code_end#}
27002760 {#see_also|comptime|enum|@compileError|Compile Variables#}
......@@ -7630,6 +7690,7 @@ fn bar(f: *Foo) void {
76307690 f.float = 12.34;
76317691}
76327692 {#code_end#}
7693 {#see_also|union|extern union#}
76337694 {#header_close#}
76347695
76357696 {#header_open|Out of Bounds Float to Integer Cast#}