diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 4e7beba17d..a4b48bbc1c 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -276,18 +276,67 @@ pub fn set(comptime T: type, dest: []T, value: T) void { d.* = value; } +/// Generally, Zig users are encouraged to explicitly initialize all fields of a struct explicitly rather than using this function. +/// However, it is recognized that there are sometimes use cases for initializing all fields to a "zero" value. For example, when +/// interfacing with a C API where this practice is more common and relied upon. If you are performing code review and see this +/// function used, examine closely - it may be a code smell. /// Zero initializes the type. -/// This can be used to zero initialize a C-struct. +/// This can be used to zero initialize a any type for which it makes sense. Structs will be initialized recursively. pub fn zeroes(comptime T: type) T { - if (@sizeOf(T) == 0) return T{}; - - if (comptime meta.containerLayout(T) != .Extern) { - @compileError("TODO: Currently this only works for extern types"); + switch (@typeInfo(T)) { + .ComptimeInt, .Int, .ComptimeFloat, .Float => { + return @as(T, 0); + }, + .Enum, .EnumLiteral => { + return @intToEnum(T, 0); + }, + .Void => { + return {}; + }, + .Bool => { + return false; + }, + .Optional, .Null => { + return null; + }, + .Struct => |struct_info| { + if (@sizeOf(T) == 0) return T{}; + if (comptime meta.containerLayout(T) == .Extern) { + var item: T = undefined; + @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T)); + return item; + } else { + var structure: T = undefined; + inline for (struct_info.fields) |field| { + @field(structure, field.name) = zeroes(@TypeOf(@field(structure, field.name))); + } + return structure; + } + }, + .Pointer => |ptr_info| { + switch (ptr_info.size) { + .Slice => { + return &[_]ptr_info.child{}; + }, + .C => { + return null; + }, + .One, .Many => { + @compileError("Can't set a non nullable pointer to zero."); + }, + } + }, + .Array => |info| { + var array: T = undefined; + for (array) |*element| { + element.* = zeroes(info.child); + } + return array; + }, + .Vector, .ErrorUnion, .ErrorSet, .Union, .Fn, .BoundFn, .Type, .NoReturn, .Undefined, .Opaque, .Frame, .AnyFrame, => { + @compileError("Can't set a "++ @typeName(T) ++" to zero."); + }, } - - var item: T = undefined; - @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T)); - return item; } test "mem.zeroes" { @@ -301,6 +350,62 @@ test "mem.zeroes" { testing.expect(a.x == 0); testing.expect(a.y == 10); + + const ZigStruct = struct { + const IntegralTypes = struct { + integer_0: i0, + integer_8: i8, + integer_16: i16, + integer_32: i32, + integer_64: i64, + integer_128: i128, + unsigned_0: u0, + unsigned_8: u8, + unsigned_16: u16, + unsigned_32: u32, + unsigned_64: u64, + unsigned_128: u128, + + float_32: f32, + float_64: f64, + }; + + integral_types: IntegralTypes, + + const Pointers = struct { + optional: ?*u8, + c_pointer: [*c]u8, + slice: []u8, + }; + pointers: Pointers, + + array: [2]u32, + optional_int: ?u8, + empty: void, + }; + + const b = zeroes(ZigStruct); + testing.expectEqual(@as(i8, 0), b.integral_types.integer_0); + testing.expectEqual(@as(i8, 0), b.integral_types.integer_8); + testing.expectEqual(@as(i16, 0), b.integral_types.integer_16); + testing.expectEqual(@as(i32, 0), b.integral_types.integer_32); + testing.expectEqual(@as(i64, 0), b.integral_types.integer_64); + testing.expectEqual(@as(i128, 0), b.integral_types.integer_128); + testing.expectEqual(@as(u8, 0), b.integral_types.unsigned_0); + testing.expectEqual(@as(u8, 0), b.integral_types.unsigned_8); + testing.expectEqual(@as(u16, 0), b.integral_types.unsigned_16); + testing.expectEqual(@as(u32, 0), b.integral_types.unsigned_32); + testing.expectEqual(@as(u64, 0), b.integral_types.unsigned_64); + testing.expectEqual(@as(u128, 0), b.integral_types.unsigned_128); + testing.expectEqual(@as(f32, 0), b.integral_types.float_32); + testing.expectEqual(@as(f64, 0), b.integral_types.float_64); + testing.expectEqual(@as(?*u8, null), b.pointers.optional); + testing.expectEqual(@as([*c]u8, null), b.pointers.c_pointer); + testing.expectEqual(@as([]u8, &[_]u8{}), b.pointers.slice); + for (b.array) |e| { + testing.expectEqual(@as(u32, 0), e); + } + testing.expectEqual(@as(?u8, null), b.optional_int); } pub fn secureZero(comptime T: type, s: []T) void {