we can unwrap multiple optionals using the helper function that takes a tuple and use destructuring for convenience
var opt_a: ?i32 = null;
var opt_b: ?f32 = 2.2;
if (unwrapAll(.{ opt_a, opt_b })) |unwrapped| {
var a, var b = unwrapped;
std.debug.print("a = {}, b = {}\n", .{ a, b });
} else {
std.debug.print("unwrap failed", .{});
}
It was fun to implement it using refiy type
fn UnwrappedType(comptime T: type) type {
switch (@typeInfo(T)) {
.Struct => |struct_info| {
var unwrapped_fields: [struct_info.fields.len]std.builtin.Type.StructField = undefined;
inline for (struct_info.fields, 0..) |field, i| {
switch (@typeInfo(field.type)) {
.Optional => |field_info| {
unwrapped_fields[i] = .{
.name = field.name,
.type = field_info.child,
.default_value = field.default_value,
.is_comptime = field.is_comptime,
.alignment = 0,
};
},
else => @compileError("all fields must be optional type!"),
}
}
return @Type(.{
.Struct = .{
.layout = .Auto,
.fields = &unwrapped_fields,
.decls = &.{},
.is_tuple = true,
},
});
},
else => @compileError("parameter must be struct type!"),
}
}
fn unwrapAll(tuple: anytype) ?UnwrappedType(@TypeOf(tuple)) {
var result: UnwrappedType(@TypeOf(tuple)) = undefined;
inline for (tuple, 0..) |opt_field, i| {
if (opt_field) |field| {
result[i] = field;
} else {
break;
}
} else {
return result;
}
return null;
}