authorgravatar for michael.bartnett@gmail.comMichael Bartnett <michael.bartnett@gmail.com> 2023-01-16 04:24:47-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-01-16 14:24:47+02:00
log31a2b8c3642f1240a70d78203d568051d4dbcd3f
tree00164723421c72b1feb39dada34bd705736045ce
parent99febb54d331ded5026024a29f8407c2d5149762
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std: Handle field struct defaults in std.mem.zeroInit

I originally started monkeying with this because std.mem.zeroes doesn't support sentinel-terminated const slices even with defaults in 0.10.x. I see that std.mem.zeroes was modified in #13256 to allow setting these slices to "". That got me partway to where I wanted, but there was still an issue fields whose types are structs, they wouldn't get their defaults. So when iterating struct fields looking for default values, when there is no default value and the type is .Struct, it will delegate to a call to zeroInit. * Initialize struct fields in zeroInit exactly once In my changes, similar to the previous implementation, the priority order for fields being initialized is: 1. If the `init` argument is a tuple, the nth element corresponding to the nth field of the struct. 2. Otherwise, if the `init` argument is not a tuple, try to find a matching field name on `init` and use that field. 3. Is the field has a default value, initalize with that value. 4. Fall back to what the field would have been initialized to via a recursive call to `std.mem.zeroInit`. But instead of initializing a default instance of the struct and then running multiple passes over it, the init method is chosen per-field and each field is initialized exactly once.

1 files changed, 54 insertions(+), 22 deletions(-)

lib/std/mem.zig+54-22
......@@ -431,34 +431,48 @@ pub fn zeroInit(comptime T: type, init: anytype) T {
431431 .Struct => |struct_info| {
432432 switch (@typeInfo(Init)) {
433433 .Struct => |init_info| {
434 var value = std.mem.zeroes(T);
435
436 inline for (struct_info.fields) |field| {
437 if (field.default_value) |default_value_ptr| {
438 const default_value = @ptrCast(*align(1) const field.type, default_value_ptr).*;
439 @field(value, field.name) = default_value;
440 }
441 }
442
443434 if (init_info.is_tuple) {
444 inline for (init_info.fields) |field, i| {
445 @field(value, struct_info.fields[i].name) = @field(init, field.name);
435 if (init_info.fields.len > struct_info.fields.len) {
436 @compileError("Tuple initializer has more elments than there are fields in `" ++ @typeName(T) ++ "`");
437 }
438 } else {
439 inline for (init_info.fields) |field| {
440 if (!@hasField(T, field.name)) {
441 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
442 }
446443 }
447 return value;
448444 }
449445
450 inline for (init_info.fields) |field| {
451 if (!@hasField(T, field.name)) {
452 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
446 var value: T = undefined;
447
448 inline for (struct_info.fields) |field, i| {
449 if (field.is_comptime) {
450 continue;
453451 }
454452
455 switch (@typeInfo(field.type)) {
456 .Struct => {
457 @field(value, field.name) = zeroInit(field.type, @field(init, field.name));
458 },
459 else => {
460 @field(value, field.name) = @field(init, field.name);
461 },
453 if (init_info.is_tuple and init_info.fields.len > i) {
454 @field(value, field.name) = @field(init, init_info.fields[i].name);
455 } else if (@hasField(@TypeOf(init), field.name)) {
456 switch (@typeInfo(field.type)) {
457 .Struct => {
458 @field(value, field.name) = zeroInit(field.type, @field(init, field.name));
459 },
460 else => {
461 @field(value, field.name) = @field(init, field.name);
462 },
463 }
464 } else if (field.default_value) |default_value_ptr| {
465 const default_value = @ptrCast(*align(1) const field.type, default_value_ptr).*;
466 @field(value, field.name) = default_value;
467 } else {
468 switch (@typeInfo(field.type)) {
469 .Struct => {
470 @field(value, field.name) = std.mem.zeroInit(field.type, .{});
471 },
472 else => {
473 @field(value, field.name) = std.mem.zeroes(@TypeOf(@field(value, field.name)));
474 },
475 }
462476 }
463477 }
464478
......@@ -538,6 +552,24 @@ test "zeroInit" {
538552 .foo = 69,
539553 .bar = 420,
540554 }, b);
555
556 const Baz = struct {
557 foo: [:0]const u8 = "bar",
558 };
559
560 const baz1 = zeroInit(Baz, .{});
561 try testing.expectEqual(Baz{}, baz1);
562
563 const baz2 = zeroInit(Baz, .{ .foo = "zab" });
564 try testing.expectEqualSlices(u8, "zab", baz2.foo);
565
566 const NestedBaz = struct {
567 bbb: Baz,
568 };
569 const nested_baz = zeroInit(NestedBaz, .{});
570 try testing.expectEqual(NestedBaz{
571 .bbb = Baz{},
572 }, nested_baz);
541573}
542574
543575/// Compares two slices of numbers lexicographically. O(n).