diff options
| author | Veikka Tuominen <git@vexu.eu> | 2022-09-27 17:38:01 +0300 |
|---|---|---|
| committer | Veikka Tuominen <git@vexu.eu> | 2022-09-27 18:33:23 +0300 |
| commit | 17eea918aee98ca29c3762a7ecd568d2f14f66ef (patch) | |
| tree | 13813b8c43ddb1d120ed29ba2dd92e354e1a205c /doc/langref.html.in | |
| parent | d4917957ef5aad5a0f381d21040a81c5afe12718 (diff) | |
| download | zig-17eea918aee98ca29c3762a7ecd568d2f14f66ef.tar.gz zig-17eea918aee98ca29c3762a7ecd568d2f14f66ef.zip | |
langref: document inline switch
Diffstat (limited to 'doc/langref.html.in')
| -rw-r--r-- | doc/langref.html.in | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/doc/langref.html.in b/doc/langref.html.in index 816d1fe32a..e5559ff33e 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4255,6 +4255,134 @@ test "enum literals with switch" { } {#code_end#} {#header_close#} + + {#header_open|Inline switch#} + <p> + Switch prongs can be marked as {#syntax#}inline{#endsyntax#} to generate + the prong's body for each possible value it could have: + </p> + {#code_begin|test|test_inline_switch#} +const std = @import("std"); +const expect = std.testing.expect; +const expectError = std.testing.expectError; + +fn isFieldOptional(comptime T: type, field_index: usize) !bool { + const fields = @typeInfo(T).Struct.fields; + return switch (field_index) { + // This prong is analyzed `fields.len - 1` times with `idx` being an + // unique comptime known value each time. + inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].field_type) == .Optional, + else => return error.IndexOutOfBounds, + }; +} + +const Struct1 = struct { a: u32, b: ?u32 }; + +test "using @typeInfo with runtime values" { + var index: usize = 0; + try expect(!try isFieldOptional(Struct1, index)); + index += 1; + try expect(try isFieldOptional(Struct1, index)); + index += 1; + try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index)); +} + +// Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent +// of this function: +fn isFieldOptionalUnrolled(field_index: usize) !bool { + return switch (field_index) { + 0 => false, + 1 => true, + else => return error.IndexOutOfBounds, + }; +} + {#code_end#} + <p> + {#syntax#}inline else{#endsyntax#} prongs can be used as a type safe + alternative to {#syntax#}inline for{#endsyntax#} loops: + </p> + {#code_begin|test|test_inline_else#} +const std = @import("std"); +const expect = std.testing.expect; + +const SliceTypeA = extern struct { + len: usize, + ptr: [*]u32, +}; +const SliceTypeB = extern struct { + ptr: [*]SliceTypeA, + len: usize, +}; +const AnySlice = union(enum) { + a: SliceTypeA, + b: SliceTypeB, + c: []const u8, + d: []AnySlice, +}; + +fn withFor(any: AnySlice) usize { + const Tag = @typeInfo(AnySlice).Union.tag_type.?; + inline for (@typeInfo(Tag).Enum.fields) |field| { + // With `inline for` the function gets generated as + // a series of `if` statements relying on the optimizer + // to convert it to a switch. + if (field.value == @enumToInt(any)) { + return @field(any, field.name).len; + } + } + // When using `inline for` the compiler doesn't know that every + // possible case has been handled requiring an explicit `unreachable`. + unreachable; +} + +fn withSwitch(any: AnySlice) usize { + return switch (any) { + // With `inline else` the function is explicitly generated + // as the desired switch and the compiler can check that + // every possible case is handled. + inline else => |slice| slice.len, + }; +} + +test "inline for and inline else similarity" { + var any = AnySlice{ .c = "hello" }; + try expect(withFor(any) == 5); + try expect(withSwitch(any) == 5); +} + {#code_end#} + <p> + When using an inline prong switching on an union an additional + capture can be used to obtain the union's enum tag value. + </p> + {#code_begin|test|test_inline_switch_union_tag#} +const std = @import("std"); +const expect = std.testing.expect; + +const U = union(enum) { + a: u32, + b: f32, +}; + +fn getNum(u: U) u32 { + switch (u) { + // Here `num` is a runtime known value that is either + // `u.a` or `u.b` and `tag` is `u`'s comptime known tag value. + inline else => |num, tag| { + if (tag == .b) { + return @floatToInt(u32, num); + } + return num; + } + } +} + +test "test" { + var u = U{ .b = 42 }; + try expect(getNum(u) == 42); +} + {#code_end#} + {#see_also|inline while|inline for#} + {#header_close#} {#header_close#} {#header_open|while#} |
