偶遇

某个周末,去往图书馆的早晨,偶然间发现了 zig 觉得很有意思,那天下午就看了一下他的 futures,可惜没有细细品味

正式学习

暑假过了十天左右才有时间步入 zig 的学习(这时才把他加上日程),本来以为 2 天 能搞定的 ziglings 硬生生地拖了 4.5 天(看来还是对有 c++ 功底这件事太自信了)

ziglings

这是一个非常适合入门的项目(除了 Async 部分没有(zig还没有正式支持), 其他都可以浅浅地入门)

地址: ziglings
答案见其中的 patch
我补充完的版本(Async 那里不知道是否正确): ziglings-mine

重要

Builtin functions

首先,所有的 builtin functions 都遵循原则: 使用 @ 前缀
同时, 很多都有使用 comptime 变量

@This

@This() type

适用于匿名 struct 引用自身

struct {
    const Self = @This();

    items: []u8,

    fn length(self: Self) usize {
        return self.items.len;
    }
};

@src

@src() std.builtin.SourceLocation
test "@src" {
    try doTheTest();
}

fn doTheTest() !void {
    const src = @src();

    try expect(src.line == 9);
    try expect(src.column == 17);
    try expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
    try expect(std.mem.endsWith(u8, src.file, "test_src_builtin.zig"));
}

@as

@as(comptime T: type, expression) T

类型强制转换函数, 类似于 cpp*_cast<>

@typeInfo

@typeInfo(comptime T: type) std.builtin.Type

@typeName

@typeName(T: type) *const [N:0]u8

@TypeOf

@TypeOf(...) type
test "no runtime side effects" {
    var data: i32 = 0;
    const T = @TypeOf(foo(i32, &data));
    try comptime expect(T == i32);
    try expect(data == 0);
}

fn foo(comptime T: type, ptr: *T) T {
    ptr.* += 1;
    return ptr.*;
}

@field

@field(lhs: anytype, comptime field_name: []const u8) (field)
const Point = struct {
    x: u32,
    y: u32,

    pub var z: u32 = 1;
};

test "field access by string" {
    var p = Point{ .x = 0, .y = 0 };

    @field(p, "x") = 4;
    @field(p, "y") = @field(p, "x") + 1;

    try expect(@field(p, "x") == 4);
    try expect(@field(p, "y") == 5);
}

test "decl access by string" {
    try expect(@field(Point, "z") == 1);

    @field(Point, "z") = 2;
    try expect(@field(Point, "z") == 2);
}

@hasDecl

@hasDecl(comptime Container: type, comptime name: []const u8) bool

检测 declaration

const Foo = struct {
    nope: i32,

    pub var blah = "xxx";
    const hi = 1;
};

test "@hasDecl" {
    try expect(@hasDecl(Foo, "blah"));

    // Even though `hi` is private, @hasDecl returns true because this test is
    // in the same file scope as Foo. It would return false if Foo was declared
    // in a different file.
    try expect(@hasDecl(Foo, "hi"));

    // @hasDecl is for declarations; not fields.
    try expect(!@hasDecl(Foo, "nope"));
    try expect(!@hasDecl(Foo, "nope1234"));
}

@hasField

@hasField(comptime Container: type, comptime name: []const u8) bool

检测 field

@embedFile

@embedFile(comptime path: []const u8) *const [N:0]u8

插入文件

@memcpy

@memcpy(noalias dest, noalias source) void

@memset

@memset(dest, elem) void

@export

@export(declaration, comptime options: std.builtin.ExportOptions) void

导出 c 语言库

comptime {
    @export(internalName, .{ .name = "foo", .linkage = .Strong });
}

fn internalName() callconv(.C) void {}

等价于

export fn foo() void {}

@select

@select(comptime T: type, pred: @Vector(len, bool), a: @Vector(len, T), b: @Vector(len, T)) @Vector(len, T)

ab 取值

test "select" {
    const pred = @Vector(3, bool){ true, false, true };
    const a = @Vector(3, u8){ 1, 1, 1 };
    const b = @Vector(3, u8){ 0, 0, 0 };

    try expectEqual(@select(u1, pred, a, b), @Vector(3, u8){ 1, 0, 1 });
}

@call

@call(modifier: std.builtin.CallModifier, function: anytype, args: anytype) anytype
fn add(a: i32, b: i32) i32 {
    return a + b;
}

test "inline function call" {
    try expect(@call(.always_inline, add, .{ 3, 9 }) == 12);
}

@bitCast

@bitCast(value: anytype) anytype

类型转换函数

test "bitCast" {
    const a: i2 = -1;
    const b: u2 = @bitCast(a);

    try expect(b == 3);
}

@addrSpaceCast

@addrSpaceCast(ptr: anytype) anytype

作用: 将指针从一个地址空间转换为另一个地址空间。(根据结果类型推断)
注意: 此强制转换可能是无操作、复杂操作或非法。
如果强制转换是合法的,则生成的指针指向与指针操作数相同的内存位置。在同一地址空间之间强制转换指针始终有效。

@divExact

@divExact(numerator: T, denominator: T) T

保证遵循(否则报错):

  1. @divExact(6, 3) == 2
  2. @divExact(a, b) * b == a
test "divExact" {
    try expect(@divExact(4, 2) * 2 == 4);
}

@addWithOverflow

@addWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }

执行 a + b 并返回包含结果和可能的溢出位的元组。

test "addWithOverflow" {
    const a: u1 = 1;
    const b: u1 = 1;
    const c: struct { @TypeOf(a, b), u1 } = @addWithOverflow(a, b);

    try expectEqual(c, .{ 0, 1 });
}

@mulWithOverflow

@mulWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }

@bitOffsetOf

@bitOffsetOf(comptime T: type, comptime field_name: []const u8) comptime_int

位偏移量

test "bitOffsetOf" {
    const MyStruct = struct {
        field1: bool,
        field2: u7,
        field3: u15,
    };
    const offsetField1 = @bitOffsetOf(MyStruct, "field1");
    const offsetField2 = @bitOffsetOf(MyStruct, "field2");
    const offsetField3 = @bitOffsetOf(MyStruct, "field3");

    try expect(offsetField1 == 16 and offsetField2 == 24 and offsetField3 == 0);

    const MyPackedStruct = packed struct {
        field1: bool,
        field2: u7,
        field3: u15,
    };

    const offsetField4 = @bitOffsetOf(MyPackedStruct, "field1");
    const offsetField5 = @bitOffsetOf(MyPackedStruct, "field2");
    const offsetField6 = @bitOffsetOf(MyPackedStruct, "field3");

    try expect(offsetField4 == 0 and offsetField5 == 1 and offsetField6 == 8);
}

@offsetOf

@offsetOf(comptime T: type, comptime field_name: []const u8) comptime_int

字节偏移量

@bitSizeOf

@bitSizeOf(comptime T: type) comptime_int

@bitReverse

@bitReverse(integer: anytype) T

@cImport

@cImport(expression) type

分析并导入 c 语言有关的 expression
在其中可以使用 @cInclude @cDefine @cUndef

为了防止clang的多次调用, 并防止内联函数被复制, 整个程序应该至多有一个 @cImport
但特殊情况使用多个可以达到以下好处:

  1. 避免符号冲突
  2. 使用不同的预处理器定义分析 C 代码

@cDefine

@cDefine(comptime name: []const u8, value) void

此功能只能在 中 @cImport 发生。
这会追加 #define $name $value 到 @cImport 临时缓冲区。

test "cDefine" {
    const c = @cImport({
        @cDefine("TEST", "1");
    });
    try expect(c.TEST == 1);
}

@clz

@clz(operand: anytype) anytype

计算前导零

operand: integer | vector

test "clz" {
    try expect(@clz(@as(u2, 1)) == 1);
}

@ctz

@ctz(operand: anytype) anytype

清除尾随零

@compileError

@compileError(comptime msg: []const u8) noreturn

@compileLog

@compileLog(args: ...) void

@breakpoint

@breakpoint() void

插入断点

@trap

@trap() noreturn

陷阱/干扰指令, 异常退出程序

@alignCast

@alignCast(ptr: anytype) anytype

ptr 可以是 *T 、 ?*T 或 []T 。更改指针的对齐方式。要使用的对齐方式是根据结果类型推断的。
将指针对齐安全检查添加到生成的代码中,以确保指针按承诺对齐。

@alignOf

@alignOf(comptime T: type) comptime_int

此函数返回此类型应对齐的字节数,以便当前目标与 C ABI 匹配。当指针的子类型具有此对齐方式时,可以从类型中省略该对齐方式。

const assert = @import("std").debug.assert;
comptime {
    assert(*u32 == *align(@alignOf(u32)) u32);
}

归纳 Futures

没有意外情况的话,按照 ziglearn 的叙述顺序来讲

Assignment

Assignment
(const|var) identifier[: type] = value

想必无需多言,毕竟这种 Assignment 已经见怪不怪了罢!
当然,一般来说 type 可以自动推导。

identifier

注意,这里的 identifier 可以支持更多的写法

const @"if" = 1;
const @"1" = @"if";
const @"1 == 2" = false;

只需要 以 @"[identifier]" 的写法就可以随性书写!

Arrays

以下全部遵循如下规则: T:type
有关更多的东西参照 Slices, Pointers

[N]T可以创建一个Arrays, 同时,[_]T可以使其自动推导长度

If

这里与 c++ 有很大区别
if语句只支持一种情况的判断: true/false
又,根据 zig 不会进行隐式类型转换(int等类型不会自动转换为bool, 相反,这时 if 会判断其是否不是 null)

值得指出的是,if 语句可以直接当作表达式使用(其他所有语句皆如此,会特殊说明)

const @"if true" = if (true) 1 else 2;