| 1 | const std = @import("std"); |
| 2 | const expectEqual = std.testing.expectEqual; |
| 3 | |
| 4 | const SliceTypeA = extern struct { |
| 5 | len: usize, |
| 6 | ptr: [*]u32, |
| 7 | }; |
| 8 | const SliceTypeB = extern struct { |
| 9 | ptr: [*]SliceTypeA, |
| 10 | len: usize, |
| 11 | }; |
| 12 | const AnySlice = union(enum(u8)) { |
| 13 | a: SliceTypeA, |
| 14 | b: SliceTypeB, |
| 15 | c: []const u8, |
| 16 | d: []AnySlice, |
| 17 | }; |
| 18 | |
| 19 | fn withFor(any: AnySlice) usize { |
| 20 | const Tag = @typeInfo(AnySlice).@"union".tag_type.?; |
| 21 | const info = @typeInfo(Tag).@"enum"; |
| 22 | inline for (info.field_names, info.field_values) |field_name, field_value| { |
| 23 | // With `inline for` the function gets generated as |
| 24 | // a series of `if` statements relying on the optimizer |
| 25 | // to convert it to a switch. |
| 26 | if (field_value == @backingInt(any)) { |
| 27 | return @field(any, field_name).len; |
| 28 | } |
| 29 | } |
| 30 | // When using `inline for` the compiler doesn't know that every |
| 31 | // possible case has been handled requiring an explicit `unreachable`. |
| 32 | unreachable; |
| 33 | } |
| 34 | |
| 35 | fn withSwitch(any: AnySlice) usize { |
| 36 | return switch (any) { |
| 37 | // With `inline else` the function is explicitly generated |
| 38 | // as the desired switch and the compiler can check that |
| 39 | // every possible case is handled. |
| 40 | inline else => |slice| slice.len, |
| 41 | }; |
| 42 | } |
| 43 | |
| 44 | test "inline for and inline else similarity" { |
| 45 | const any = AnySlice{ .c = "hello" }; |
| 46 | try expectEqual(5, withFor(any)); |
| 47 | try expectEqual(5, withSwitch(any)); |
| 48 | } |
| 49 | |
| 50 | // test |