Commit 17eea918ae

Veikka Tuominen <git@vexu.eu>
2022-09-27 16:38:01
langref: document inline switch
1 parent d491795
Changed files (1)
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#}