3

Zig 初体验

 2 years ago
source link: https://liujiacai.net/blog/2022/07/16/zig-intro/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文涉及代码可在这个 GitHub 仓库找到。

Hello World

首先看看 Zig 语言官网上的例子,来切身感受一下这个语言的样子。

// 引入标准库
const std = @import("std");
const json = std.json;
// 定义多行的字符串
const payload =
    \\{
    \\    "vals": {
    \\        "testing": 1,
    \\        "production": 42
    \\    },
    \\    "uptime": 9999
    \\}
;
// 定义一个结构体类型,类型和 u8 等基础类型一样是一等成员,可以作为值来使用
const Config = struct {
    vals: struct { testing: u8, production: u8 },
    uptime: u64,
};
// 定义一个全局变量,不可变,同时定义了一个 block,label 为 x
// 由于 config 是个 const,因此该 block 会在编译期执行
const config = x: {
    // 使用 var 定义一个可变的局部变量
    var stream = json.TokenStream.init(payload);
    const res = json.parse(Config, &stream, .{});
    break :x res catch unreachable;
};

// main 函数的返回值为 !void,这里省略了具体错误类型,由编译器自动推导
pub fn main() !void {
    if (config.vals.production > 50) {
        @compileError("only up to 50 supported");
    }
    std.log.info("up={d}", .{config.uptime});
}

上面代码加了部分注释,算是对 Zig 语法的简单介绍,下面重点介绍 json 解析部分的语法:

  • json.parse 的函数签名是

    parse(comptime T: type, tokens: *TokenStream, options: ParseOptions) ParseError(T)!T
    
    • 第一个参数中的 comptime 关键字表示该参数是在编译期时执行,T 的类型是 type,表示类型值

    • 返回值 ParseError(T)!T 表示返回值可能出错,类似 Rust 中的 Result 类型

      • ParseError(T) 会在编译期执行,得出具体错误类型。Zig 采用 comptime 来实现泛型

  • json.parse(Config, &stream, .{}) 中的 .{} 表示匿名结构体,类型可以由编译器推导出来,结构体内没有对字段进行初始化,是由于 ParseOptions 中的字符都有默认值

  • break :x 表示退出 block 快,后面跟着返回值

  • res catch unreachable 表示取出正常情况下的值,相当于 Rust 中的 res.unwrap()

上面这个示例代码展示了 Zig 中一些特有的语法,最主要的是 comptime,它是 Zig 实现泛型的基础,类型是一等成员,可以进行函数调用或赋值。比如下面这个例子:

fn LinkedList(comptime T: type) type {
    return struct {
        pub const Node = struct {
            // ?T 表示 Option 类型,值可能为 null
            prev: ?*Node,
            next: ?*Node,
            data: T,
        };

        first: ?*Node,
        last:  ?*Node,
        len:   usize,
    };
}

这里的 LinkedList 定义了一个泛型链表,类型在编译期确定。使用方式如下:

// test 定义了一个测试代码快,zig test main.zig 时会执行这里面的代码
test "linked list" {
    // Functions called at compile-time are memoized. This means you can
    // do this:
    // expect 可能返回错误,try 表示 catch |err| return err
    // 类似于 Rust 中的 result?
    try expect(LinkedList(i32) == LinkedList(i32));

    var list = LinkedList(i32) {
        .first = null,
        .last = null,
        .len = 0,
    };
    try expect(list.len == 0);

    // Since types are first class values you can instantiate the type
    // by assigning it to a variable:
    const ListOfInts = LinkedList(i32);
    try expect(ListOfInts == LinkedList(i32));

    var node = ListOfInts.Node {
        .prev = null,
        .next = null,
        .data = 1234,
    };
    var list2 = LinkedList(i32) {
        .first = &node,
        .last = &node,
        .len = 1,
    };

    // When using a pointer to a struct, fields can be accessed directly,
    // without explicitly dereferencing the pointer.
    // So you can do
    // option.? 相当于 rust 中的 option.unwrap(),取出其中的值
    try expect(list2.first.?.data == 1234);
    // instead of try expect(list2.first.?.*.data == 1234);
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK