| ... | ... | @@ -2046,6 +2046,13 @@ test "linked list" { |
| 2046 | 2046 | assert(list2.first.?.data == 1234); |
| 2047 | 2047 | } |
| 2048 | 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 | 2056 | {#header_open|packed struct#} |
| 2050 | 2057 | <p> |
| 2051 | 2058 | Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout: |
| ... | ... | @@ -2412,12 +2419,32 @@ test "packed enum" { |
| 2412 | 2419 | {#see_also|@memberName|@memberCount|@tagName|@sizeOf#} |
| 2413 | 2420 | {#header_close#} |
| 2414 | 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 | 2445 | const std = @import("std"); |
| 2417 | 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 | 2448 | const Payload = union { |
| 2422 | 2449 | Int: i64, |
| 2423 | 2450 | Float: f64, |
| ... | ... | @@ -2425,14 +2452,25 @@ const Payload = union { |
| 2425 | 2452 | }; |
| 2426 | 2453 | test "simple union" { |
| 2427 | 2454 | var payload = Payload{ .Int = 1234 }; |
| 2428 | | // payload.Float = 12.34; // ERROR! field not active |
| 2429 | 2455 | assert(payload.Int == 1234); |
| 2430 | | // You can activate another field by assigning the entire union. |
| 2431 | 2456 | payload = Payload{ .Float = 12.34 }; |
| 2432 | 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 | 2474 | const ComplexTypeTag = enum { |
| 2437 | 2475 | Ok, |
| 2438 | 2476 | NotOk, |
| ... | ... | @@ -2442,56 +2480,68 @@ const ComplexType = union(ComplexTypeTag) { |
| 2442 | 2480 | NotOk: void, |
| 2443 | 2481 | }; |
| 2444 | 2482 | |
| 2445 | | // Declare a specific instance of the union variant. |
| 2446 | | test "declare union value" { |
| 2447 | | const c = ComplexType{ .Ok = 0 }; |
| 2483 | test "switch on tagged union" { |
| 2484 | const c = ComplexType{ .Ok = 42 }; |
| 2448 | 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 | 2493 | test "@TagType" { |
| 2453 | 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. |
| 2457 | | const Foo = union(enum) { |
| 2458 | | String: []const u8, |
| 2459 | | Number: u64, |
| 2460 | | |
| 2461 | | // void can be omitted when inferring enum tag type. |
| 2462 | | None, |
| 2504 | const ComplexTypeTag = enum { |
| 2505 | Ok, |
| 2506 | NotOk, |
| 2507 | }; |
| 2508 | const ComplexType = union(ComplexTypeTag) { |
| 2509 | Ok: u8, |
| 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 |
| 2473 | | Foo.Number => |x| blk: { |
| 2474 | | assert(x == 54); |
| 2475 | | break :blk "this is a number"; |
| 2476 | | }, |
| 2513 | test "modify tagged union in switch" { |
| 2514 | var c = ComplexType{ .Ok = 42 }; |
| 2515 | assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); |
| 2477 | 2516 | |
| 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 | } |
| 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 | 2533 | const Variant = union(enum) { |
| 2488 | 2534 | Int: i32, |
| 2489 | 2535 | Bool: bool, |
| 2490 | 2536 | |
| 2537 | // void can be omitted when inferring enum tag type. |
| 2538 | None, |
| 2539 | |
| 2491 | 2540 | fn truthy(self: Variant) bool { |
| 2492 | 2541 | return switch (self) { |
| 2493 | 2542 | Variant.Int => |x_int| x_int != 0, |
| 2494 | 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 | 2553 | assert(v1.truthy()); |
| 2504 | 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 | 2565 | const Small2 = union(enum) { |
| 2526 | 2566 | A: i32, |
| 2527 | 2567 | B: bool, |
| 2528 | 2568 | C: u8, |
| 2529 | 2569 | }; |
| 2530 | 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 | 2573 | {#code_end#} |
| 2574 | {#header_close#} |
| 2575 | |
| 2576 | {#header_open|extern union#} |
| 2534 | 2577 | <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. |
| 2537 | 2580 | </p> |
| 2581 | {#see_also|extern struct#} |
| 2582 | {#header_close#} |
| 2583 | |
| 2538 | 2584 | {#header_open|packed union#} |
| 2539 | 2585 | <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible |
| 2540 | 2586 | to be in a {#link|packed struct#}. |
| ... | ... | @@ -2623,7 +2669,7 @@ test "switch simple" { |
| 2623 | 2669 | |
| 2624 | 2670 | // Ranges can be specified using the ... syntax. These are inclusive |
| 2625 | 2671 | // both ends. |
| 2626 | | 5 ... 100 => 1, |
| 2672 | 5...100 => 1, |
| 2627 | 2673 | |
| 2628 | 2674 | // Branches can be arbitrarily complex. |
| 2629 | 2675 | 101 => blk: { |
| ... | ... | @@ -2649,14 +2695,47 @@ test "switch simple" { |
| 2649 | 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 | 2732 | const Item = union(enum) { |
| 2654 | 2733 | A: u32, |
| 2655 | | C: struct { x: u8, y: u8 }, |
| 2734 | C: Point, |
| 2656 | 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 | 2740 | // Switching on more complex enums is allowed. |
| 2662 | 2741 | const b = switch (a) { |
| ... | ... | @@ -2674,27 +2753,8 @@ test "switch enum" { |
| 2674 | 2753 | Item.D => 8, |
| 2675 | 2754 | }; |
| 2676 | 2755 | |
| 2677 | | assert(b == 3); |
| 2678 | | } |
| 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 | | } |
| 2756 | assert(b == 6); |
| 2757 | assert(a.C.x == 2); |
| 2698 | 2758 | } |
| 2699 | 2759 | {#code_end#} |
| 2700 | 2760 | {#see_also|comptime|enum|@compileError|Compile Variables#} |
| ... | ... | @@ -7630,6 +7690,7 @@ fn bar(f: *Foo) void { |
| 7630 | 7690 | f.float = 12.34; |
| 7631 | 7691 | } |
| 7632 | 7692 | {#code_end#} |
| 7693 | {#see_also|union|extern union#} |
| 7633 | 7694 | {#header_close#} |
| 7634 | 7695 | |
| 7635 | 7696 | {#header_open|Out of Bounds Float to Integer Cast#} |