Zig从XX到放弃(4)null和undefined
null
null
的类型表达了一个可选项,用?T
来表示这个值可以是类型T
,也可以是null
。这个类型的值可以通过?
操作符来访问,这个操作符会检查这个值是否为null
,如果是的话,就会panic。
如果对null
调用@typeOf
,会返回@TypeOf(null)
。
null
在语法层次有很多的支持。比如对可选值可以进行如下的操作得到默认值。
const x: ?u8 = null;
const x_opt = x orelse 10;
一个例子
这个例子模拟了一个u8
队列,这个队列的接口很简单,hasNext()
,next()
,reset()
, get()
。每个函数的的意义不言自明。
// simulate a little queue, but with bad apples, represented with null.
const u8Queue = struct {
const ar = [_]?u8{ 1, 2, 3, 4, null, 5, 6, null };
var index: usize = 0;
fn hasNext() bool {
return index < ar.len;
}
fn next() void {
index += 1;
}
fn reset() void {
index = 0;
}
fn get() ?u8 {
return ar[index];
}
};
在消费这个队列的?u8
的程序里面,有几种方式。
第一种,打印数字,碰到第一个null
就停止。这是while () || {} else {}
的一种语法。当捕捉到payload
(这个术语描述可选值包含的有意义的值),执行第一个分支;当碰到null
则执行第二个分支,并结束循环。
第二方式,采用while () : () {}
的语法获取所有的数字。在循环内部,采用if () || {} else {}
的分支语法进行处理。
这里还注释掉引发panic
的调用方式,程序运行时会输出:thread 8836 panic: attempt to use null value
。
第三种方式就是利用orelse
直接为null
提供一个默认值。
const std = @import("std");
pub fn main() !void {
// 1.
// Stop with the first bad apple
u8Queue.reset();
while (u8Queue.get()) |val| {
std.debug.print("{}\t", .{val});
u8Queue.next();
} else {
std.debug.print("null\n", .{});
}
// 2.
// test for bad apple and print them with null
u8Queue.reset();
while (u8Queue.hasNext()) : (u8Queue.next()) {
// const v = u8Queue.getCurrentAr().?; // try to get payload of a optional value
// std.debug.print("{d}", .{v}); // this will cause panic immediately!
if (u8Queue.get()) |val| {
std.debug.print("{}\t", .{val});
} else {
std.debug.print("null\t", .{});
}
}
std.debug.print("End\n", .{});
// 3.
// Get a default value 0 for bad apples
u8Queue.reset();
while (u8Queue.hasNext()) : (u8Queue.next()) {
const val = u8Queue.get() orelse 0;
std.debug.print("{}\t", .{val});
}
std.debug.print("End.\n", .{});
}
程序输出的结果:
1 2 3 4 null
1 2 3 4 null 5 6 null End
1 2 3 4 0 5 6 0 End.
undefined
undefined
可以被强制转换为任何类型。一旦发生这种情况,就不再可能检测到该值是未定义的。未定义意味着该值可以是任何值,甚至是根据类型来说是无意义的东西。翻译成人话,未定义的意思是“不是有意义的值。使用这个值将是一个bug。该值将不会被使用,或者在使用之前被覆盖。”
var x: i32 = undefined;
var y: [10]u8 = undefined;
var z: ?u8 = undefined;
在Debug模式下,Zig会将0xaa字节写入未定义的内存。这是为了尽早捕获错误,并帮助在调试器中检测使用未定义的内存。然而,这种行为只是一种实现特性,而不是一种语言语义,因此不能保证对代码是可观察的。
总之一句话,undefined
的类型可以是任何类型;在这个值被访问时,就会报错。
总结
null
比较有用,用于表示不正常的值,构成可选类型?T
;- 配合
while
,if
的特殊语法,挺方便的; undefined
用途单一,就是临时不初始化一个值。