| ... | @@ -2046,6 +2046,13 @@ test "linked list" { | ... | @@ -2046,6 +2046,13 @@ test "linked list" { |
| 2046 | assert(list2.first.?.data == 1234); | 2046 | assert(list2.first.?.data == 1234); |
| 2047 | } | 2047 | } |
| 2048 | {#code_end#} | 2048 | {#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 | |
| 2049 | {#header_open|packed struct#} | 2056 | {#header_open|packed struct#} |
| 2050 | <p> | 2057 | <p> |
| 2051 | Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout: | 2058 | Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout: |
| ... | @@ -2412,12 +2419,32 @@ test "packed enum" { | ... | @@ -2412,12 +2419,32 @@ test "packed enum" { |
| 2412 | {#see_also|@memberName|@memberCount|@tagName|@sizeOf#} | 2419 | {#see_also|@memberName|@memberCount|@tagName|@sizeOf#} |
| 2413 | {#header_close#} | 2420 | {#header_close#} |
| 2414 | {#header_open|union#} | 2421 | {#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#} |
| | 2433 | const Payload = union { |
| | 2434 | Int: i64, |
| | 2435 | Float: f64, |
| | 2436 | Bool: bool, |
| | 2437 | }; |
| | 2438 | test "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#} |
| 2416 | const std = @import("std"); | 2445 | const std = @import("std"); |
| 2417 | const assert = std.debug.assert; | 2446 | const assert = std.debug.assert; |
| 2418 | const mem = std.mem; | | |
| 2419 | | 2447 | |
| 2420 | // A union has only 1 active field at a time. | | |
| 2421 | const Payload = union { | 2448 | const Payload = union { |
| 2422 | Int: i64, | 2449 | Int: i64, |
| 2423 | Float: f64, | 2450 | Float: f64, |
| ... | @@ -2425,14 +2452,25 @@ const Payload = union { | ... | @@ -2425,14 +2452,25 @@ const Payload = union { |
| 2425 | }; | 2452 | }; |
| 2426 | test "simple union" { | 2453 | test "simple union" { |
| 2427 | var payload = Payload{ .Int = 1234 }; | 2454 | var payload = Payload{ .Int = 1234 }; |
| 2428 | // payload.Float = 12.34; // ERROR! field not active | | |
| 2429 | assert(payload.Int == 1234); | 2455 | assert(payload.Int == 1234); |
| 2430 | // You can activate another field by assigning the entire union. | | |
| 2431 | payload = Payload{ .Float = 12.34 }; | 2456 | payload = Payload{ .Float = 12.34 }; |
| 2432 | assert(payload.Float == 12.34); | 2457 | assert(payload.Float == 12.34); |
| 2433 | } | 2458 | } |
| | 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#} |
| | 2471 | const std = @import("std"); |
| | 2472 | const assert = std.debug.assert; |
| 2434 | | 2473 | |
| 2435 | // Unions can be given an enum tag type: | | |
| 2436 | const ComplexTypeTag = enum { | 2474 | const ComplexTypeTag = enum { |
| 2437 | Ok, | 2475 | Ok, |
| 2438 | NotOk, | 2476 | NotOk, |
| ... | @@ -2442,56 +2480,68 @@ const ComplexType = union(ComplexTypeTag) { | ... | @@ -2442,56 +2480,68 @@ const ComplexType = union(ComplexTypeTag) { |
| 2442 | NotOk: void, | 2480 | NotOk: void, |
| 2443 | }; | 2481 | }; |
| 2444 | | 2482 | |
| 2445 | // Declare a specific instance of the union variant. | 2483 | test "switch on tagged union" { |
| 2446 | test "declare union value" { | 2484 | const c = ComplexType{ .Ok = 42 }; |
| 2447 | const c = ComplexType{ .Ok = 0 }; | | |
| 2448 | assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); | 2485 | assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); |
| | 2486 | |
| | 2487 | switch (c) { |
| | 2488 | ComplexTypeTag.Ok => |value| assert(value == 42), |
| | 2489 | ComplexTypeTag.NotOk => unreachable, |
| | 2490 | } |
| 2449 | } | 2491 | } |
| 2450 | | 2492 | |
| 2451 | // @TagType can be used to access the enum tag type of a tagged union. | | |
| 2452 | test "@TagType" { | 2493 | test "@TagType" { |
| 2453 | assert(@TagType(ComplexType) == ComplexTypeTag); | 2494 | assert(@TagType(ComplexType) == ComplexTypeTag); |
| 2454 | } | 2495 | } |
| | 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#} |
| | 2501 | const std = @import("std"); |
| | 2502 | const assert = std.debug.assert; |
| 2455 | | 2503 | |
| 2456 | // Unions can be made to infer the enum tag type. | 2504 | const ComplexTypeTag = enum { |
| 2457 | const Foo = union(enum) { | 2505 | Ok, |
| 2458 | String: []const u8, | 2506 | NotOk, |
| 2459 | Number: u64, | 2507 | }; |
| 2460 | | 2508 | const ComplexType = union(ComplexTypeTag) { |
| 2461 | // void can be omitted when inferring enum tag type. | 2509 | Ok: u8, |
| 2462 | None, | 2510 | NotOk: void, |
| 2463 | }; | 2511 | }; |
| 2464 | test "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 | }, | | |
| 2471 | | 2512 | |
| 2472 | // Capture by value | 2513 | test "modify tagged union in switch" { |
| 2473 | Foo.Number => |x| blk: { | 2514 | var c = ComplexType{ .Ok = 42 }; |
| 2474 | assert(x == 54); | 2515 | assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); |
| 2475 | break :blk "this is a number"; | | |
| 2476 | }, | | |
| 2477 | | 2516 | |
| 2478 | Foo.None => blk: { | 2517 | switch (c) { |
| 2479 | break :blk "this is a none"; | 2518 | ComplexTypeTag.Ok => |*value| value.* += 1, |
| 2480 | }, | 2519 | ComplexTypeTag.NotOk => unreachable, |
| 2481 | }; | 2520 | } |
| 2482 | assert(mem.eql(u8, what_is_it, "this is a number")); | | |
| 2483 | } | | |
| 2484 | | 2521 | |
| 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#} |
| | 2530 | const std = @import("std"); |
| | 2531 | const assert = std.debug.assert; |
| 2486 | | 2532 | |
| 2487 | const Variant = union(enum) { | 2533 | const Variant = union(enum) { |
| 2488 | Int: i32, | 2534 | Int: i32, |
| 2489 | Bool: bool, | 2535 | Bool: bool, |
| 2490 | | 2536 | |
| | 2537 | // void can be omitted when inferring enum tag type. |
| | 2538 | None, |
| | 2539 | |
| 2491 | fn truthy(self: Variant) bool { | 2540 | fn truthy(self: Variant) bool { |
| 2492 | return switch (self) { | 2541 | return switch (self) { |
| 2493 | Variant.Int => |x_int| x_int != 0, | 2542 | Variant.Int => |x_int| x_int != 0, |
| 2494 | Variant.Bool => |x_bool| x_bool, | 2543 | Variant.Bool => |x_bool| x_bool, |
| | 2544 | Variant.None => false, |
| 2495 | }; | 2545 | }; |
| 2496 | } | 2546 | } |
| 2497 | }; | 2547 | }; |
| ... | @@ -2503,38 +2553,34 @@ test "union method" { | ... | @@ -2503,38 +2553,34 @@ test "union method" { |
| 2503 | assert(v1.truthy()); | 2553 | assert(v1.truthy()); |
| 2504 | assert(!v2.truthy()); | 2554 | assert(!v2.truthy()); |
| 2505 | } | 2555 | } |
| | 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#} |
| | 2562 | const std = @import("std"); |
| | 2563 | const assert = std.debug.assert; |
| 2506 | | 2564 | |
| 2507 | const Small = union { | | |
| 2508 | A: i32, | | |
| 2509 | B: bool, | | |
| 2510 | C: u8, | | |
| 2511 | }; | | |
| 2512 | | | |
| 2513 | // @memberCount tells how many fields a union has: | | |
| 2514 | test "@memberCount" { | | |
| 2515 | assert(@memberCount(Small) == 3); | | |
| 2516 | } | | |
| 2517 | | | |
| 2518 | // @memberName tells the name of a field in an enum: | | |
| 2519 | test "@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. | | |
| 2525 | const Small2 = union(enum) { | 2565 | const Small2 = union(enum) { |
| 2526 | A: i32, | 2566 | A: i32, |
| 2527 | B: bool, | 2567 | B: bool, |
| 2528 | C: u8, | 2568 | C: u8, |
| 2529 | }; | 2569 | }; |
| 2530 | test "@tagName" { | 2570 | test "@tagName" { |
| 2531 | assert(mem.eql(u8, @tagName(Small2.C), "C")); | 2571 | assert(std.mem.eql(u8, @tagName(Small2.C), "C")); |
| 2532 | } | 2572 | } |
| 2533 | {#code_end#} | 2573 | {#code_end#} |
| | 2574 | {#header_close#} |
| | 2575 | |
| | 2576 | {#header_open|extern union#} |
| 2534 | <p> | 2577 | <p> |
| 2535 | Unions with an enum tag are generated as a struct with a tag field and union field. Zig | 2578 | An {#syntax#}extern union{#endsyntax#} has memory layout guaranteed to be compatible with |
| 2536 | sorts the order of the tag and union field by the largest alignment. | 2579 | the target C ABI. |
| 2537 | </p> | 2580 | </p> |
| | 2581 | {#see_also|extern struct#} |
| | 2582 | {#header_close#} |
| | 2583 | |
| 2538 | {#header_open|packed union#} | 2584 | {#header_open|packed union#} |
| 2539 | <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible | 2585 | <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible |
| 2540 | to be in a {#link|packed struct#}. | 2586 | to be in a {#link|packed struct#}. |
| ... | @@ -2623,7 +2669,7 @@ test "switch simple" { | ... | @@ -2623,7 +2669,7 @@ test "switch simple" { |
| 2623 | | 2669 | |
| 2624 | // Ranges can be specified using the ... syntax. These are inclusive | 2670 | // Ranges can be specified using the ... syntax. These are inclusive |
| 2625 | // both ends. | 2671 | // both ends. |
| 2626 | 5 ... 100 => 1, | 2672 | 5...100 => 1, |
| 2627 | | 2673 | |
| 2628 | // Branches can be arbitrarily complex. | 2674 | // Branches can be arbitrarily complex. |
| 2629 | 101 => blk: { | 2675 | 101 => blk: { |
| ... | @@ -2649,14 +2695,47 @@ test "switch simple" { | ... | @@ -2649,14 +2695,47 @@ test "switch simple" { |
| 2649 | assert(b == 1); | 2695 | assert(b == 1); |
| 2650 | } | 2696 | } |
| 2651 | | 2697 | |
| 2652 | test "switch enum" { | 2698 | // Switch expressions can be used outside a function: |
| | 2699 | const 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. |
| | 2706 | test "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#} |
| | 2725 | const assert = @import("std").debug.assert; |
| | 2726 | |
| | 2727 | test "switch on tagged union" { |
| | 2728 | const Point = struct { |
| | 2729 | x: u8, |
| | 2730 | y: u8, |
| | 2731 | }; |
| 2653 | const Item = union(enum) { | 2732 | const Item = union(enum) { |
| 2654 | A: u32, | 2733 | A: u32, |
| 2655 | C: struct { x: u8, y: u8 }, | 2734 | C: Point, |
| 2656 | D, | 2735 | D, |
| 2657 | }; | 2736 | }; |
| 2658 | | 2737 | |
| 2659 | var a = Item { .A = 3 }; | 2738 | var a = Item{ .C = Point{ .x = 1, .y = 2 } }; |
| 2660 | | 2739 | |
| 2661 | // Switching on more complex enums is allowed. | 2740 | // Switching on more complex enums is allowed. |
| 2662 | const b = switch (a) { | 2741 | const b = switch (a) { |
| ... | @@ -2674,27 +2753,8 @@ test "switch enum" { | ... | @@ -2674,27 +2753,8 @@ test "switch enum" { |
| 2674 | Item.D => 8, | 2753 | Item.D => 8, |
| 2675 | }; | 2754 | }; |
| 2676 | | 2755 | |
| 2677 | assert(b == 3); | 2756 | assert(b == 6); |
| 2678 | } | 2757 | assert(a.C.x == 2); |
| 2679 | | | |
| 2680 | // Switch expressions can be used outside a function: | | |
| 2681 | const 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. | | |
| 2688 | test "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 | } | | |
| 2698 | } | 2758 | } |
| 2699 | {#code_end#} | 2759 | {#code_end#} |
| 2700 | {#see_also|comptime|enum|@compileError|Compile Variables#} | 2760 | {#see_also|comptime|enum|@compileError|Compile Variables#} |
| ... | @@ -7630,6 +7690,7 @@ fn bar(f: *Foo) void { | ... | @@ -7630,6 +7690,7 @@ fn bar(f: *Foo) void { |
| 7630 | f.float = 12.34; | 7690 | f.float = 12.34; |
| 7631 | } | 7691 | } |
| 7632 | {#code_end#} | 7692 | {#code_end#} |
| | 7693 | {#see_also|union|extern union#} |
| 7633 | {#header_close#} | 7694 | {#header_close#} |
| 7634 | | 7695 | |
| 7635 | {#header_open|Out of Bounds Float to Integer Cast#} | 7696 | {#header_open|Out of Bounds Float to Integer Cast#} |