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的类型可以是任何类型;在这个值被访问时,就会报错。

总结

  1. null比较有用,用于表示不正常的值,构成可选类型?T
  2. 配合whileif的特殊语法,挺方便的;
  3. undefined用途单一,就是临时不初始化一个值。
posted @ 2023-04-27 19:00  大福是小强  阅读(41)  评论(0编辑  收藏  举报  来源