mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 03:03:09 +00:00
langref: document inline switch
This commit is contained in:
parent
d4917957ef
commit
17eea918ae
@ -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#}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user