mcpField
Z
zig-0.16

zig-0.16

github.com/zigcc开发52228 次安装2 周前更新
代码仓库

关于 zig-0.16

zig-0.16 是面向 Zig 0.16.0 开发者的编程指南技能,聚焦该版本的语言与标准库变化。它汇总破坏性改动、迁移示例及本地文档查阅方法。

主要功能

  1. 01针对 Zig 0.16.0
  2. 02汇总语言级变更
  3. 03说明 std.Io 与标准库 API 更新
  4. 04提供旧 API 到新 API 的迁移示例
  5. 05指导查阅本地语言和标准库文档

适用场景

  1. 01将 Zig 项目迁移到 0.16.0
  2. 02核对标准库 API 的版本差异
  3. 03更新 I/O、网络或文件操作代码
  4. 04查找 Zig 0.16.0 的入口与环境变量用法

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zigcc/skills --skill zig-0.16

安装后在 Claude Code 中输入/zig-0.16确认已加载。

附带文件scripts/check-zig-version.sh

详细介绍

Zig 0.16.0 编程指南

版本范围:此技能固定使用 Zig 0.16.0(稳定版)。 发行说明:https://ziglang.org/download/0.16.0/release-notes.html

Zig 0.16.0 是一个重大版本,引入了作为统一 I/O 接口的 std.Io,移除了 @Type,弃用了 @cImport,并对构建系统的包管理进行了大幅调整。


优先查阅本地文档

运行 zig env 查找安装路径——切勿将路径硬编码。重要字段:.lib_dir、.std_dir、.version。

  • 语言参考:<lib_dir>/../doc/langref.html
  • 标准库源码:阅读 .std_dir 下的文件以验证 API。
  • 标准库文档:运行 zig std 启动本地 HTTP 服务。

搜索网页之前,务必先查阅本地文档。


快速参考:主要破坏性变更

旧版(0.15.x)新版(0.16.0)
@Type(.Int(...))@Int(.signed, bits)
@Type(.Struct(...))@Struct(layout, BackingInt, field_names, field_types, field_defaults, field_is_comptime, field_alignments)
@Type(.Pointer(...))@Pointer(size, attrs, Element, sentinel)
@Type(.Fn(...))@Fn(param_types, param_attrs, ReturnType, attrs)
@Type(.Tuple(...))@Tuple(field_types)
@cImport({...})b.addTranslateC(...) + @import("c")(已弃用)
std.netstd.Io.net
std.ArrayList.init(allocator)std.ArrayList.initCapacity(allocator, n)
std.crypto.randomstd.Io.randomSecure(io, buf)
std.meta.intToEnumstd.enums.fromInt
std.fmt.FormatOptionsstd.fmt.Options
std.fmt.bufPrintZstd.fmt.bufPrintSentinel
std.io.fixedBufferStreamstd.Io.Writer.fixed(buf)
error.RenameAcrossMountPointserror.CrossDevice
error.NotSameFileSystemerror.CrossDevice
error.SharingViolationerror.FileBusy
error.EnvironmentVariableNotFounderror.EnvironmentVariableMissing
std.Build.Step.ConfigHeader 的 cmake 处理修正了行首空白

语言变更

移除 @Type——改用独立内建函数

@Type 已移除。请替换为以下内建函数:

// Integer type
const MyInt = @Int(.signed, 32);

// Struct type
const MyStruct = @Struct(
    .auto,           // layout
    null,            // BackingInt (for packed)
    &.{"x", "y"},    // field_names
    &.{ i32, i32 },  // field_types
    &.{ null, null },// field_defaults
    &.{ false, false }, // field_is_comptime
    &.{ null, null },// field_alignments
);

// Pointer type
const MyPtr = @Pointer(.one, .{
    .alignment = 8,
    .address_space = .generic,
    .is_const = false,
    .is_volatile = false,
}, u8, null);

// Function type
const MyFn = @Fn(&.{i32, i32}, &.{.{}, .{}}, i32, .{});

// Tuple type
const MyTuple = @Tuple(&.{ i32, bool });

// Enum literal type
const EnumLitType = @EnumLiteral();
弃用 @cImport——改用构建系统转换

@cImport 已弃用。请在 build.zig 中使用 addTranslateC:

// build.zig
const translate_c = b.addTranslateC(.{
    .root_source_file = b.path("src/c.h"),
    .target = target,
    .optimize = optimize,
});
translate_c.linkSystemLibrary("glfw", .{});

const exe = b.addExecutable(.{
    .name = "myapp",
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .optimize = optimize,
        .target = target,
        .imports = &.{
            .{ .name = "c", .module = translate_c.createModule() },
        },
    }),
});

然后在 Zig 中使用:const c = @import("c");

注意:C 转换器现在基于 arocc,而非 libclang。

Switch 改进
  • packed struct 和 packed union 现在可用作 switch 分支项(按底层整数比较)。
  • decl 字面量和依赖结果类型的表达式(例如 @enumFromInt)现在可用作 switch 分支项。
  • 所有分支现在都允许捕获联合标签,不再仅限于 inline 分支。
  • 现在不能再丢弃所有 switch 分支捕获。
  • 已移除 .awake 原子操作顺序;请使用 .monotonic / .seq_cst。
其他语言层面的注意事项
  • 小整数类型可强制转换为浮点数——例如,现在允许 u8 → f32。
  • 禁止使用运行时向量索引——向量索引必须在编译时已知。
  • 向量和数组不再支持内存中的强制转换——必须逐个显式转换元素。
  • 不能从函数返回普通局部变量的地址——例如,return &local; 现在会更一致地被拒绝。
  • 打包联合不得包含未使用的位——所有位模式都必须有效。
  • 打包结构体/联合中禁止使用指针。
  • 显式对齐的指针类型现在在类型系统中与自然对齐的指针类型不同。
  • 简化了依赖循环规则——大多数代码仍可正常工作,但自引用的对齐查询现在会报错。

标准库:std.Io

网络
  • std.net 已移除。请使用 std.Io.net(类型:IpAddress、Stream、Server、UnixAddress)。
  • 高级辅助函数(如 tcpConnectToHost、Address.parseIp、Stream.connect)已移除。请使用 Io 虚函数表:io.vtable.netConnectIp、netRead、netWrite、netListenIp、netAccept。
  • 原始文件描述符操作:请使用 std.os.linux.socket、accept4、connect、bind、listen、write、read、close。
  • posix.socket、posix.bind、posix.accept、posix.shutdown、posix.writev、posix.listen、posix.pipe2、posix.getrandom、std.net.has_unix_sockets 已移除。
  • Epoll:直接使用 std.os.linux.epoll_create1/epoll_ctl/epoll_wait。
  • 新增:std.Io.net.Socket.createPair,用于测试中的 socketpair。
I/O 读取器/写入器
  • 读取器/写入器接口位于 std.Io 下。TLS 需要 *std.Io.Reader / *std.Io.Writer(结构体字段为 .interface)。请传递接口指针,而不是将 interface() 作为函数调用。
  • std.time.sleep 已移除;请使用 std.Io.sleep(io, Duration, Clock)。
  • std.Io.GenericWriter、std.Io.AnyWriter、std.Io.null_writer、std.Io.CountingReader 已移除。
时间 / 随机数 / 环境变量 / 退出
  • std.time.milliTimestamp 已移除。请使用 std.time.Timer 或 std.Io.Clock.now(clock, io),并比较 Timestamp.nanoseconds。
  • 安全随机字节:使用 std.Io.randomSecure(io, buf);没有 std.crypto.random 或 std.posix.getrandom 便捷函数。
  • std.process.getEnvVarOwned 已移除;请使用 std.c.getenv 并复制结果。
  • std.posix.exit 已移除;请使用 std.process.exit。
TLS 客户端选项
  • std.crypto.tls.Client.Options 需要 entropy: *const [entropy_len]u8 和 realtime_now_seconds: i64。请使用 std.Io.randomSecure 填充熵,并通过 Io.Clock.now(.real, io) / ns_per_s 计算秒数。
MemoryPool API 变更
  • std.heap.MemoryPool(T).initCapacity(allocator, n) 会返回该池。
  • create/destroy 现在需要分配器。不再有无参 init() 或无参 deinit()。
格式化选项
  • std.fmt.Options 取代了 FormatOptions。
  • std.fmt.format 辅助函数已移除;请直接调用 writer.print(写入器位于 std.Io.Writer 中)。
  • std.fmt.Formatter 更名为 Alt。
  • std.fmt.bufPrintZ 更名为 std.fmt.bufPrintSentinel。
随机数 / 加密
  • std.crypto.random 已移除。请使用 std.Io 实例:const io = std.Io.Threaded.global_single_threaded.ioBasic(); io.random(&buf);。
  • Ed25519.KeyPair.generate 现在需要一个 io: std.Io 参数。
枚举转换
  • std.meta.intToEnum 已移除。请使用 std.enums.fromInt(EnumType, value)(返回 ?EnumType)。
  • meta.declList 已移除。
测试中的固定缓冲区写入器
  • std.io.fixedBufferStream 已移除。要进行内存写入,请使用 var w = std.Io.Writer.fixed(buf);,并通过 std.Io.Writer.buffered(&w) 读取字节。
  • std.ArrayList 不再提供 .init(allocator) 简写;请使用 .initCapacity(allocator, n)。
集合
  • SegmentedList 已移除。
  • BitSet、EnumSet:将 initEmpty、initFull 替换为声明字面量(例如 .{}、.{ .a = true })。
  • 新增:Io.Dir.renamePreserve — 重命名时不替换目标。
  • std.Io.Dir.rename 返回 error.DirNotEmpty,而不是 error.PathAlreadyExists。
错误集合重命名
// Old -> New
error.RenameAcrossMountPoints -> error.CrossDevice
error.NotSameFileSystem       -> error.CrossDevice
error.SharingViolation        -> error.FileBusy
error.EnvironmentVariableNotFound -> error.EnvironmentVariableMissing
数学
  • math.sign 现在会返回能够容纳所有可能值的最小整数类型。
线程
  • Thread.Mutex.Recursive 已移除。
压缩
  • lzma、lzma2、xz 已更新为使用 Io.Reader / Io.Writer 接口。
动态库
  • DynLib 已移除对 Windows 的支持。请直接使用 LoadLibraryExW 和 GetProcAddress。

新的执行模型:Juicy Main

Zig 0.16.0 引入了 std.process.Init,作为首选的 main 参数。这是此版本中入口点方面最大的变更。

main 签名
// 1. Bare (no args/env available)
pub fn main() !void { }

// 2. Minimal (raw argv + environ only)
pub fn main(init: std.process.Init.Minimal) !void { }

// 3. Juicy Main (recommended)
pub fn main(init: std.process.Init) !void { }
std.process.Init 内容
pub const Init = struct {
    minimal: Minimal,
    arena: *std.heap.ArenaAllocator,  // process-lifetime arena, threadsafe
    gpa: Allocator,                     // general-purpose allocator
    io: Io,                             // default Io implementation
    environ_map: *Environ.Map,          // env vars (not threadsafe)
    preopens: Preopens,                 // WASI-style preopened files

    pub const Minimal = struct {
        environ: Environ,
        args: Args,
    };
};
示例
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;
    const io = init.io;

    try std.Io.File.stdout().writeStreamingAll(io, "Hello, world!\n");

    const args = try init.minimal.args.toSlice(init.arena.allocator());
    for (args, 0..) |arg, i| {
        std.log.info("arg[{d}] = {s}", .{ i, arg });
    }

    std.log.info("{d} env vars", .{init.environ_map.count()});
}

经验法则:如果程序需要 I/O、内存分配或环境变量,请使用 std.process.Init。


环境变量和进程参数不再是全局状态

std.os.environ 和 std.process.args 不再作为全局状态存在。现在只能通过 main 的 init 参数访问。

环境变量
// Juicy Main
for (init.environ_map.keys(), init.environ_map.values()) |key, value| {
    std.log.info("env: {s}={s}", .{ key, value });
}

// Minimal main
std.log.info("HOME: {?s}", .{init.environ.getPosix("HOME")});
std.log.info("EDITOR: {s}", .{ try init.environ.getAlloc(arena, "EDITOR") });
const map = try init.environ.createMap(arena);
CLI 参数
// Iterate lazily
var args = init.args.iterate();
while (args.next()) |arg| {
    std.log.info("arg: {s}", .{arg});
}

// To slice (needs allocator)
const args = try init.args.toSlice(arena);

任何需要环境变量的函数都应将 *const process.Environ.Map 或 process.Environ 作为参数。


移除 Thread.Pool → 使用 std.Io

std.Thread.Pool 已移除。请替换为 std.Io.Group、std.Io.async 或 std.Io.concurrent。

迁移示例
// OLD (0.15)
fn doAllTheWork(pool: *std.Thread.Pool) void {
    var wg: std.Thread.WaitGroup = .{};
    pool.spawnWg(wg, doSomeWork, .{ pool, &wg, first_work_item });
    wg.wait();
}

// NEW (0.16)
fn doAllTheWork(io: std.Io) void {
    var g: std.Io.Group = .init;
    errdefer g.cancel(io);
    g.async(io, doSomeWork, .{ io, &g, first_work_item });
    g.wait(io);
}

fn doSomeWork(io: std.Io, g: *std.Io.Group, foo: Foo) void {
    foo.doTheThing();
    for (foo.new_work_items) |new| {
        g.async(io, doSomeWork, .{ io, g, new });
    }
}

重要:从 Thread.Pool 迁移到 Io 时,请将 Thread.Mutex、Thread.Condition、Thread.ResetEvent 转换为 Io.Mutex、Io.Condition、Io.Event。


文件 I/O 和 fs API 变更

readFileAlloc / readToEndAlloc
// OLD
const contents = try std.fs.cwd().readFileAlloc(allocator, file_name, 1234);

// NEW
const contents = try std.Io.Dir.cwd().readFileAlloc(io, file_name, allocator, .limited(1234));
// Note: FileTooBig -> StreamTooLong when limit reached.
// OLD
const contents = try file.readToEndAlloc(allocator, 1234);

// NEW
var file_reader = file.reader(&.{});
const contents = try file_reader.interface.allocRemaining(allocator, .limited(1234));
setTimestamps
// OLD
try atomic_file.file_writer.file.setTimestamps(io, src_stat.atime, src_stat.mtime);

// NEW
try atomic_file.file_writer.file.setTimestamps(io, .{
    .access_timestamp = .init(src_stat.atime),
    .modify_timestamp = .init(src_stat.mtime),
});

Io.File.Stat.atime 现在是 ?Timestamp(例如,在 ZFS 上可能为 null)。

目录遍历
  • std.Io.Dir.walk 仍然存在。
  • 新增:std.Io.Dir.walkSelectively——可选择是否递归遍历各目录,避免冗余的打开/关闭系统调用。
  • Walker 和 SelectiveWalker 均新增了 leave() 和 depth()。
fs.path.relative 现在是纯函数
// OLD
const relative = try std.fs.path.relative(gpa, from, to);

// NEW
const cwd_path = try std.process.currentPathAlloc(io, gpa);
const relative = try std.fs.path.relative(gpa, cwd_path, environ_map, from, to);
fs.path Windows 变更
  • windowsParsePath/diskDesignator/diskDesignatorWindows → parsePath、parsePathWindows、parsePathPosix
  • 新增 getWin32PathType
  • componentIterator/ComponentIterator.init 不再可能失败
当前目录 API 更名
// OLD
std.process.getCwd(buffer)
std.process.getCwdAlloc(allocator)

// NEW
std.process.currentPath(io, buffer)
std.process.currentPathAlloc(io, allocator)

预打开文件与原子/临时文件

预打开文件
// OLD (WASI)
const wasi_preopens: std.fs.wasi.Preopens = try .preopensAlloc(arena);

// NEW
const preopens: std.process.Preopens = try .init(arena);
// Or simply use init.preopens from Juicy Main.

在非 WASI 目标上,Preopens 是 void——零开销抽象。

原子/临时文件

std.Io.File.Atomic 是用于原子写入文件和处理临时文件的新 API。

  • Linux:在可能的情况下集成 O_TMPFILE。
  • 新增:std.Io.File.hardLink
  • 临时/随机文件名现在通过 Io 虚函数表生成,而不是通过 std.crypto.random 生成。

内存和分配器变更

ArenaAllocator 现在无锁且线程安全

std.heap.ArenaAllocator 不再需要用 ThreadSafeAllocator 包装。单线程性能相近,在争用情况下性能更快(约 7 个线程)。

移除 ThreadSafe Allocator

std.heap.ThreadSafe 已移除;请直接使用 ArenaAllocator,或手动同步访问。

内存锁定/保护已移至 std.process
// OLD
try std.posix.mlock();
try std.posix.mlock2(slice, std.posix.MLOCK_ONFAULT);
try std.posix.mlockall(slice, std.posix.MCL_CURRENT | std.posix.MCL_FUTURE);

// NEW
try std.process.lockMemory(slice, .{});
try std.process.lockMemory(slice, .{ .on_fault = true });
try std.process.lockMemoryAll(.{ .current = true, .future = true });

mmap/mprotect 标志现在是类型安全的结构体,而不是按位 OR:

// OLD
std.posix.PROT.READ | std.posix.PROT.WRITE

// NEW
.{ .READ = true, .WRITE = true }
MemoryPool
  • std.heap.MemoryPool(T).initCapacity(allocator, n) 返回该池。
  • create/destroy 现在需要分配器参数。
  • 新增非托管变体:MemoryPoolUnmanaged、MemoryPoolAlignedUnmanaged、MemoryPoolExtraUnmanaged。
Io.Writer.Allocating 对齐字段
alignment: std.mem.Alignment,  // new field

容器和集合变更

迁移到“Unmanaged”

带有内嵌 Allocator 字段的托管变体正在逐步淘汰。

  • ArrayHashMap、AutoArrayHashMap、StringArrayHashMap 已移除。
  • AutoArrayHashMapUnmanaged → array_hash_map.Auto
  • StringArrayHashMapUnmanaged → array_hash_map.String
  • ArrayHashMapUnmanaged → array_hash_map.Custom
PriorityQueue
var queue = std.PriorityQueue(u32, void, lessThan).empty;
  • init → initContext
  • add → push
  • addSlice → pushSlice
  • remove/removeOrNull → pop
  • removeIndex → popIndex
PriorityDequeue
var dq = std.PriorityDequeue(u32, void, lessThan).empty;
  • init → .empty
  • add → push
  • removeMinOrNull/removeMin → popMin
  • removeMaxOrNull/removeMax → popMax
  • removeIndex → popIndex
BitSet / EnumSet

使用声明字面量,而不是 initEmpty/initFull:

var set = std.EnumSet(MyEnum).empty;
var full = std.EnumSet(MyEnum).full;
SegmentedList
  • std.SegmentedList 已移除。

已移除的 API(快速列表)

已移除的 API替代方案
std.Thread.Poolstd.Io.Group、Io.async、Io.concurrent
std.heap.ThreadSafestd.heap.ArenaAllocator(现已线程安全)
std.io.fixedBufferStreamstd.Io.Reader.fixed(data) / std.Io.Writer.fixed(buf)
std.Io.GenericReaderstd.Io.Reader
std.Io.AnyReaderstd.Io.Reader
std.Io.GenericWriterstd.Io.Writer
std.Io.AnyWriterstd.Io.Writer
std.Io.null_writer直接使用 std.Io.Writer
std.Io.CountingReader没有直接替代方案;手动跟踪字节数
std.SegmentedList无
std.meta.declList无
std.fs.getAppDataDir无
std.posix.getCwd*std.process.currentPath*
std.posix.mlock*std.process.lockMemory*
std.posix.PROT_* 位标志结构体字面量标志
std.ArrayHashMaparray_hash_map.Auto / .String / .Custom

Zig 0.16.0 中的常用习惯用法

ArrayList
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;

    var list = try std.ArrayList(u8).initCapacity(gpa, 16);
    defer list.deinit(gpa);

    try list.append(gpa, 'a');
    try list.appendSlice(gpa, "hello");
    try list.ensureTotalCapacity(gpa, 100);

    const owned = try list.toOwnedSlice(gpa);
    defer gpa.free(owned);
}
HashMap(默认/非托管风格)
var map = std.StringHashMap(u32).empty;
defer map.deinit(gpa);

try map.put(gpa, "key", 42);
const val = map.get("key") orelse 0;
使用 Juicy Main 的 stdout / stderr
pub fn main(init: std.process.Init) !void {
    const io = init.io;

    // Direct streaming write
    try std.Io.File.stdout().writeStreamingAll(io, "Hello, world!\n");

    // Or via writer interface
    var stdout_writer = std.Io.File.stdout().writer(&.{});
    try stdout_writer.interface.print("value: {d}\n", .{42});
}
固定缓冲区 Reader / Writer
// Reader from byte slice
var data = "line1\nline2\n";
var reader: std.Io.Reader = .fixed(data);

// Writer into a stack buffer
var buf: [256]u8 = undefined;
var writer: std.Io.Writer = .fixed(&buf);
try writer.interface.print("count: {d}", .{7});
文件 I/O
// Read entire file
const contents = try std.Io.Dir.cwd().readFileAlloc(io, "input.txt", gpa, .limited(1024 * 1024));
defer gpa.free(contents);

// Write file atomically
var atomic = try std.Io.File.Atomic.init(io, gpa, "output.txt");
try atomic.file_writer.interface.print("data: {s}\n", .{"hello"});
try atomic.commit(io);
JSON
const MyStruct = struct {
    name: []const u8,
    value: u32,
};

// Parsing
const json_str =
    \\{"name": "test", "value": 42}
;
const parsed = try std.json.parseFromSlice(MyStruct, gpa, json_str, .{});
defer parsed.deinit();

// Serialization to string (via Io.Writer.Allocating)
var out: std.Io.Writer.Allocating = .init(gpa);
defer out.deinit();
var stringify: std.json.Stringify = .{
    .writer = &out.writer,
    .options = .{},
};
try stringify.write(parsed.value);
const json_output = out.written();
类型反射(Comptime)
const info = @typeInfo(T);
if (info == .pointer) { ... }
if (info == .slice) { ... }
if (info == .@"struct") { ... }
if (info == .@"enum") { ... }
if (info == .@"union") { ... }
if (info == .optional) { ... }
测试
test "basic arithmetic" {
    const std = @import("std");
    try std.testing.expectEqual(4, 2 + 2);
    try std.testing.expectError(error.OutOfMemory, mightFail());
}

test "no leaks" {
    const std = @import("std");
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
    const allocator = gpa.allocator();

    const ptr = try allocator.create(u32);
    defer allocator.destroy(ptr);
}
Leb128(二进制格式)
// OLD: std.leb.readUleb128(reader)
// NEW:
const value = try reader.takeLeb128(u64);

常见错误消息及修复方法(0.16)

错误原因修复方法
no field named 'getStdOut'使用了旧版 std.io.getStdOut()使用 std.Io.File.stdout()
expected 2 argument(s), found 1ArrayList 方法缺少分配器参数将分配器作为第一个参数添加
no member named 'Pool' in 'Thread'std.Thread.Pool 已移除使用 std.Io.Group + Io.async
expected type '*const process.Environ.Map'函数需要环境映射参数从 main(init) 传入
no member named 'fixedBufferStream'旧版 stream API 已移除使用 std.Io.Reader.fixed(...) 或 std.Io.Writer.fixed(...)
no field named 'getCwd' in 'process'已重命名使用 std.process.currentPath*
fingerprint field missing使用了旧版 build.zig.zon添加 .fingerprint = 0x...,并将 .name = "x" 改为 .name = .x
type depends on itself for alignment对齐查询存在自引用移除 @alignOf(@This()) 或重构

构建系统变更

build.zig.zon 中必须包含 Fingerprint

build.zig.zon 现在必须包含 fingerprint 字段,而且 name 必须是枚举字面量,而不是字符串:

.{
    .name = .myproject,  // enum literal, not "myproject"
    .fingerprint = 0x123456789abcdef0,
    .version = "0.1.0",
    .dependencies = .{},
}

如果缺少这些内容,zig build 将会失败。

本地包覆盖:--fork

用于临时本地覆盖的新 CLI 标志:

zig build --fork=/path/to/local/fork

任何 name + fingerprint 匹配的依赖项都会解析到本地路径,而不是被获取。此设置是临时的——移除该标志即可恢复。

获取到 zig-pkg 目录的包

依赖项现在会获取到本地 zig-pkg/ 目录(与 build.zig 位于同一目录),而不是全局缓存。筛选后,它们会被重新压缩到全局缓存,以便日后复用。

单元测试超时

为每项测试指定超时时间:

zig build test --test-timeout 500ms

超时后,测试进程会被终止,并为下一项测试重新启动。

ConfigHeader
  • std.Build.Step.ConfigHeader 现在可以正确处理 cmake 风格配置头文件开头的空白字符。

编译器/工具链

C 翻译
  • @cImport 仍然存在,但已弃用,现在由 arocc 提供支持,而非 libclang。
  • 对于新代码,始终在 build.zig 中使用 addTranslateC。
类型解析
  • 内部类型解析已重做。此前大多数可用代码仍然可用,部分“依赖循环”错误现已解决。
  • 自引用对齐查询(例如 align(@alignOf(@This())))现在会正确报错。
LLVM 后端
  • 实验性的增量编译支持。
  • 错误集类型现在会在调试信息中降级为枚举,因此错误名称在运行时可见。

目标支持

重要新增内容:

  • aarch64-freebsd、aarch64-netbsd、loongarch64-linux、powerpc64le-linux、s390x-linux、x86_64-freebsd、x86_64-netbsd、x86_64-openbsd 现已纳入 CI 测试。
  • 新增 aarch64-maccatalyst、x86_64-maccatalyst 交叉编译支持。
  • 初步支持 loongarch32-linux(暂不支持 libc)。
  • 新增对 Alpha、KVX、MicroBlaze、OpenRISC、PA-RISC、SuperH 的基本支持。
  • 移除 Solaris、AIX、z/OS 支持。
  • 几乎所有主要目标平台的堆栈跟踪均已改进。

测试调整

  • 对于进程内客户端/服务端测试,请使用 std.Io.net.Socket.createPair 或原始 socketpair(AF.UNIX, SOCK.STREAM|CLOEXEC, 0, &fds),避免依赖 Io vtable 网络(未接线的 Threaded nets 会返回 NetworkDown)。
  • 许多 std.Io 函数的错误集已收紧,请相应移除不可达分支。

移植策略(0.15 → 0.16)

  1. 将 @Type 调用替换为相应的新内置函数。
  2. 将 @cImport 替换为 build.zig 中的 addTranslateC。
  3. 在 build.zig.zon 中添加 fingerprint 并修正 name。
  4. 在应用中贯穿传递 std.Io——任何执行 I/O、休眠、随机数生成或时间操作的函数都需要 io 参数。
  5. 将 std.net 用法更新为 std.Io.net 或原始系统调用。
  6. 更新 ArrayList 调用,显式传入分配器并使用 initCapacity。
  7. 修正错误集名称(CrossDevice、FileBusy、EnvironmentVariableMissing、DirNotEmpty)。
  8. 运行 zig build test --test-timeout 500ms,尽早发现可能卡住的测试。

编写新的 Zig 0.16.0 代码或从 0.15.x 升级时,请使用此技能。

评论

支持 Markdown