I have a function specificPrintIsNull
which performs as expected, testing if an parameter with optional type is null or not, but the genericPrintIsNull
always says the value is not null. I don’t understand why there is a difference.
const std = @import("std");
fn genericPrintIsNull(comptime T: type, v: ?T) void {
if (v == null) {
std.debug.print("It is null\n", .{});
} else {
std.debug.print("It is NOT null\n", .{});
}
}
fn specificPrintIsNull(v: ?u8) void {
if (v == null) {
std.debug.print("It is null\n", .{});
} else {
std.debug.print("It is NOT null\n", .{});
}
}
pub fn main() void {
var i: ?u8 = null;
genericPrintIsNull(@TypeOf(i), i);
specificPrintIsNull(i);
i = 42;
std.debug.print("After assignment\n", .{});
genericPrintIsNull(@TypeOf(i), i);
specificPrintIsNull(i);
}
Here @TypeOf(i) = ?u8
. In genericPrintIsNull
we now have T=?u8
and thus it expects a second parameter of type ?(?u8)
. By providing a parameter of type ?u8
, it considers the parameter to be non-null.
In short: double optionals make syntax quite confusing.
1 Like
Yes, it makes sense. Here is the fixed version, thanks:
const std = @import("std");
fn genericPrintIsNull(comptime T: type, v: ?T) void {
if (v == null) {
std.debug.print("It is null\n", .{});
} else {
std.debug.print("It is NOT null\n", .{});
}
}
fn specificPrintIsNull(v: ?u8) void {
if (v == null) {
std.debug.print("It is null\n", .{});
} else {
std.debug.print("It is NOT null\n", .{});
}
}
pub fn main() void {
var i: ?u8 = null;
genericPrintIsNull(u8, i);
specificPrintIsNull(i);
i = 42;
std.debug.print("After assignment\n", .{});
genericPrintIsNull(u8, i);
specificPrintIsNull(i);
}
Side question: is there a predefined function to “unwrap” the type of an optional variable and get the base type? (For instance, @BaseOf(?u8) → u8.) I don’t find it Documentation - The Zig Programming Language
@typeInfo(?u8).Optional.child
is your friend.
2 Likes
Also std.meta.Child
to be less verbose (and works on anything with a child type).
3 Likes