| ... | @@ -4255,6 +4255,134 @@ test "enum literals with switch" { | ... | @@ -4255,6 +4255,134 @@ test "enum literals with switch" { |
| 4255 | } | 4255 | } |
| 4256 | {#code_end#} | 4256 | {#code_end#} |
| 4257 | {#header_close#} | 4257 | {#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#} |
| | 4265 | const std = @import("std"); |
| | 4266 | const expect = std.testing.expect; |
| | 4267 | const expectError = std.testing.expectError; |
| | 4268 | |
| | 4269 | fn 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 | |
| | 4279 | const Struct1 = struct { a: u32, b: ?u32 }; |
| | 4280 | |
| | 4281 | test "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: |
| | 4292 | fn 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#} |
| | 4305 | const std = @import("std"); |
| | 4306 | const expect = std.testing.expect; |
| | 4307 | |
| | 4308 | const SliceTypeA = extern struct { |
| | 4309 | len: usize, |
| | 4310 | ptr: [*]u32, |
| | 4311 | }; |
| | 4312 | const SliceTypeB = extern struct { |
| | 4313 | ptr: [*]SliceTypeA, |
| | 4314 | len: usize, |
| | 4315 | }; |
| | 4316 | const AnySlice = union(enum) { |
| | 4317 | a: SliceTypeA, |
| | 4318 | b: SliceTypeB, |
| | 4319 | c: []const u8, |
| | 4320 | d: []AnySlice, |
| | 4321 | }; |
| | 4322 | |
| | 4323 | fn 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 | |
| | 4338 | fn 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 | |
| | 4347 | test "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#} |
| | 4358 | const std = @import("std"); |
| | 4359 | const expect = std.testing.expect; |
| | 4360 | |
| | 4361 | const U = union(enum) { |
| | 4362 | a: u32, |
| | 4363 | b: f32, |
| | 4364 | }; |
| | 4365 | |
| | 4366 | fn 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 | |
| | 4379 | test "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#} |
| 4258 | {#header_close#} | 4386 | {#header_close#} |
| 4259 | | 4387 | |
| 4260 | {#header_open|while#} | 4388 | {#header_open|while#} |