authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-27 17:38:01+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-27 18:33:23+03:00
log17eea918aee98ca29c3762a7ecd568d2f14f66ef
tree13813b8c43ddb1d120ed29ba2dd92e354e1a205c
parentd4917957ef5aad5a0f381d21040a81c5afe12718

langref: document inline switch


1 files changed, 128 insertions(+), 0 deletions(-)

doc/langref.html.in+128
......@@ -4255,6 +4255,134 @@ test "enum literals with switch" {
42554255}
42564256 {#code_end#}
42574257 {#header_close#}
4258
4259 {#header_open|Inline switch#}
4260 <p>
4261 Switch prongs can be marked as {#syntax#}inline{#endsyntax#} to generate
4262 the prong's body for each possible value it could have:
4263 </p>
4264 {#code_begin|test|test_inline_switch#}
4265const std = @import("std");
4266const expect = std.testing.expect;
4267const expectError = std.testing.expectError;
4268
4269fn isFieldOptional(comptime T: type, field_index: usize) !bool {
4270 const fields = @typeInfo(T).Struct.fields;
4271 return switch (field_index) {
4272 // This prong is analyzed `fields.len - 1` times with `idx` being an
4273 // unique comptime known value each time.
4274 inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].field_type) == .Optional,
4275 else => return error.IndexOutOfBounds,
4276 };
4277}
4278
4279const Struct1 = struct { a: u32, b: ?u32 };
4280
4281test "using @typeInfo with runtime values" {
4282 var index: usize = 0;
4283 try expect(!try isFieldOptional(Struct1, index));
4284 index += 1;
4285 try expect(try isFieldOptional(Struct1, index));
4286 index += 1;
4287 try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index));
4288}
4289
4290// Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent
4291// of this function:
4292fn isFieldOptionalUnrolled(field_index: usize) !bool {
4293 return switch (field_index) {
4294 0 => false,
4295 1 => true,
4296 else => return error.IndexOutOfBounds,
4297 };
4298}
4299 {#code_end#}
4300 <p>
4301 {#syntax#}inline else{#endsyntax#} prongs can be used as a type safe
4302 alternative to {#syntax#}inline for{#endsyntax#} loops:
4303 </p>
4304 {#code_begin|test|test_inline_else#}
4305const std = @import("std");
4306const expect = std.testing.expect;
4307
4308const SliceTypeA = extern struct {
4309 len: usize,
4310 ptr: [*]u32,
4311};
4312const SliceTypeB = extern struct {
4313 ptr: [*]SliceTypeA,
4314 len: usize,
4315};
4316const AnySlice = union(enum) {
4317 a: SliceTypeA,
4318 b: SliceTypeB,
4319 c: []const u8,
4320 d: []AnySlice,
4321};
4322
4323fn withFor(any: AnySlice) usize {
4324 const Tag = @typeInfo(AnySlice).Union.tag_type.?;
4325 inline for (@typeInfo(Tag).Enum.fields) |field| {
4326 // With `inline for` the function gets generated as
4327 // a series of `if` statements relying on the optimizer
4328 // to convert it to a switch.
4329 if (field.value == @enumToInt(any)) {
4330 return @field(any, field.name).len;
4331 }
4332 }
4333 // When using `inline for` the compiler doesn't know that every
4334 // possible case has been handled requiring an explicit `unreachable`.
4335 unreachable;
4336}
4337
4338fn withSwitch(any: AnySlice) usize {
4339 return switch (any) {
4340 // With `inline else` the function is explicitly generated
4341 // as the desired switch and the compiler can check that
4342 // every possible case is handled.
4343 inline else => |slice| slice.len,
4344 };
4345}
4346
4347test "inline for and inline else similarity" {
4348 var any = AnySlice{ .c = "hello" };
4349 try expect(withFor(any) == 5);
4350 try expect(withSwitch(any) == 5);
4351}
4352 {#code_end#}
4353 <p>
4354 When using an inline prong switching on an union an additional
4355 capture can be used to obtain the union's enum tag value.
4356 </p>
4357 {#code_begin|test|test_inline_switch_union_tag#}
4358const std = @import("std");
4359const expect = std.testing.expect;
4360
4361const U = union(enum) {
4362 a: u32,
4363 b: f32,
4364};
4365
4366fn getNum(u: U) u32 {
4367 switch (u) {
4368 // Here `num` is a runtime known value that is either
4369 // `u.a` or `u.b` and `tag` is `u`'s comptime known tag value.
4370 inline else => |num, tag| {
4371 if (tag == .b) {
4372 return @floatToInt(u32, num);
4373 }
4374 return num;
4375 }
4376 }
4377}
4378
4379test "test" {
4380 var u = U{ .b = 42 };
4381 try expect(getNum(u) == 42);
4382}
4383 {#code_end#}
4384 {#see_also|inline while|inline for#}
4385 {#header_close#}
42584386 {#header_close#}
42594387
42604388 {#header_open|while#}