编程 Zig 语言十年:拒绝 1.0 的背后,是一场关于「好代码」的灵魂拷问

2026-08-12 20:45:32 +0800 CST views 9

Zig 语言十年:拒绝 1.0 的背后,是一场关于「好代码」的灵魂拷问

引言:当一个语言用十年拒绝长大

2026年,Zig 语言创始人 Andrew Kelley 在一次公开访谈中说了这么一句话:

"Zig 的目标不是取代 C,而是成为未来 50 年的通用系统编程语言。"

这句话从 2016 年说到 2026 年,十年过去了,Zig 依然没有发布 1.0 版本。对比之下,Rust 早在 2015 年就发布了 1.0,Go 更是在 2009 年就已经成型。为什么一个被这么多人寄予厚望的语言,在 1.0 这个问题上如此保守?

更耐人寻味的是,就在 2026 年 8 月,Bun 的创始人 Jarred Sumner 宣布用 AI(Claude)重写了整个 Bun 的核心代码,引来了 Andrew Kelley 的公开炮轰——"没人把关的烂代码"。这一事件把两个极端摆在了程序员面前:一端是速度至上、用 AI 批量生产代码;另一端是十年磨一剑、拒绝发布 1.0 的工匠精神。

这不只是一个技术路线之争。这背后是整个行业对「什么是好代码」「软件工程应该如何做」的根本性分歧。

本文将深入拆解:

  1. Zig 的核心设计哲学——为什么它要「少即是多」
  2. Zig 的编译模型和 comptime 机制——元编程的正确打开方式
  3. 与 C/Rust/Go 的横向对比——谁才是真正的 C 继承者
  4. Andrew Kelley 十年反思的深层含义
  5. Bun AI 重写事件的工程视角解读
  6. Zig 在 2026 年的生产环境现状
  7. 一段 Zig 实战代码:从零构建高性能 HTTP 服务器

一、Zig 的哲学底座:少即是多,不做魔法

1.1 为什么 Zig 不是「更好的 C++」

很多人第一次接触 Zig 会觉得它像是「语法现代化的 C」,但这个理解是错的。Zig 的野心不是改进 C++,而是回答一个更根本的问题:现代系统编程语言的复杂度是不是已经失控了?

看看 C++ 的演化轨迹:从 C++98 到 C++23,标准文档从几百页膨胀到两千多页。模板元编程、概念(Concepts)、协程、模块系统……每一代都在「加法」,但每加一次,就有一批程序员被甩在后面。

Rust 的回答是用所有权系统和借用检查器来保证内存安全,但这套系统本身就有陡峭的学习曲线,很多初学者被 lifetime 标注折磨得痛不欲生。

Zig 的回答是:把复杂度留给库作者,而不是语言本身。 语言层面尽可能简单,把能力下沉到标准库和 comptime(编译时计算)。

用 Andrew Kelley 自己的话说:

"你花在理解语言特性的每一分钟,都是你无法花在理解业务的分钟。我们想让程序员的时间花在真正重要的事情上。"

1.2 无隐藏控制流

Zig 最独特的设计原则之一是「无隐藏控制流」。这是什么意思?

看一个 Go 的例子:

// Go: defer 隐藏了控制流
func readFile(path string) error {
    file, err := os.Open(path)
    if err != nil {
        return err
    }
    defer file.Close()  // 控制流在这里被隐藏了

    data := make([]byte, 1024)
    _, err = file.Read(data)
    return err
}

defer 是 Go 的强大特性,但它的执行时机是「函数退出时」,这意味着控制流不是线性的——你必须在心里维护一个 deferred 操作的栈。

Zig 的做法:

// Zig: 控制流完全显式
const std = @import("std");

fn readFile(path: []const u8) !void {
    var file = try std.fs.cwd().openFile(path, .{});
    defer file.close(); // 同样是 defer,但 Zig 的哲学不同

    var buf: [1024]u8 = undefined;
    _ = try file.readAll(&buf);
}

等等,这里也是 defer。但关键区别在于:Zig 的错误处理是显式的——注意那个 !void,表示这个函数可以返回错误。在 Zig 中,错误是值,不是控制流。你不会「抛出一个错误然后被某个远处的 catch 捕获」,错误是函数签名的一部分,必须显式处理。

1.3 无空指针、无未定义行为默认值

Zig 的一条核心设计原则:null 是一个类型,必须显式处理

// Zig: ?T 表示「可能是 null 的 T」
fn findUser(id: u64) ?User {
    // 如果找不到,返回 null
    // 如果找到,返回 User
}

// 调用方必须处理两种情况
fn displayUser(id: u64) void {
    if (findUser(id)) |user| {
        std.debug.print("Found: {s}\n", .{user.name});
    } else {
        std.debug.print("User not found\n", .{});
    }
}

这个设计看起来像 Rust 的 Option<T>,但语法更轻量。关键是:在 Zig 中,「遗漏 null 检查」不是编译警告,而是编译错误。编译器强制你处理每一种可能的值。


二、comptime:Zig 的元编程心脏

2.1 什么是 comptime

Zig 最强大的特性之一是 comptime。这个关键字告诉编译器:「这段代码在编译时执行,而不是运行时」。

听起来像是 C++ 的模板元编程?但完全不是一个量级。

// 编译时计算阶乘
fn factorial(comptime n: u64) u64 {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

test "compile-time factorial" {
    // 这个 assert 在编译时求值——如果 factorial(5) != 120,编译失败
    comptime var result = factorial(5);
    try std.testing.expect(result == 120);
}

// 编译时类型生成
fn Matrix(comptime T: type, comptime rows: u32, comptime cols: u32) type {
    return struct {
        data: [rows][cols]T,

        fn get(self: *const Matrix(T, rows, cols), row: u32, col: u32) T {
            return self.data[row][col];
        }
    };
}

test "generic matrix" {
    var mat = Matrix(f32, 3, 4){ .data = undefined };
    _ = mat.get(0, 0);
}

注意 comptime varcomptime 函数的用法。在 Zig 中,你可以:

  • 在编译时声明变量
  • 调用任意函数,只要它的参数在编译时可知
  • 编译时生成类型
  • 编译时执行任意计算

2.2 comptime vs. 泛型

很多语言有泛型(Generics),Zig 没有。Zig 的答案是 comptime + 匿名结构体:

// 这是一个泛型队列——Zig 里叫「类型参数化函数」
fn Queue(comptime T: type) type {
    return struct {
        items: []T,
        len: usize,

        fn enqueue(self: *Queue(T), item: T) !void {
            // ... 实现
        }

        fn dequeue(self: *Queue(T)) ?T {
            // ... 实现
        }
    };
}

// 使用
var int_queue = Queue(i32){ .items = &.{}, .len = 0 };
var str_queue = Queue([]const u8){ .items = &.{}, .len = 0 };

Queue(i32)Queue([]const u8) 是两个不同的类型,由编译器在编译时生成。不需要单独的语法扩展,不需要类型擦除,不需要运行时多态——类型本身是编译时计算的产物

2.3 comptime 的实际威力:构建安全的 SQL 生成器

// 用 comptime 构建类型安全的 SQL 查询
fn Query(comptime table: []const u8, comptime columns: []const []const u8) type {
    return struct {
        const table_name = table;
        const selected_columns = columns;

        fn selectAll(allocator: std.mem.Allocator) ![][]const u8 {
            // 编译时构建 SQL
            var sql: [256]u8 = undefined;
            var fbs = std.io.fixedBufferStream(&sql);

            try fbs.writer().print("SELECT ", .{});
            inline for (columns, 0..) |col, i| {
                if (i > 0) try fbs.writer().print(", ", .{});
                try fbs.writer().print("{s}", .{col});
            }
            try fbs.writer().print(" FROM {s}", .{table});

            // 此时 SQL 已经在编译时验证为合法语法
            return try db.query(allocator, fbs.getWritten());
        }
    };
}

// 编译时验证列名——如果 users 表没有 "invalid_column",编译失败
const UserQuery = Query("users", &.{ "id", "name", "email" });

inline for 是另一个 comptime 特性——它告诉编译器「在编译时展开这个循环」。这样,即使是在类型层面执行的逻辑,也能享受循环的便利。


三、构建一个高性能 HTTP 服务器

3.1 项目结构

// src/main.zig
const std = @import("std");
const net = std.net;
const StreamServer = net.StreamServer;
const Address = net.Address;

pub const Config = struct {
    host: []const u8 = "0.0.0.0",
    port: u16 = 8080,
    workers: u32 = 4,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(!gpa.deinit());
    const allocator = gpa.allocator();

    const config = Config{
        .host = "0.0.0.0",
        .port = 8080,
    };

    var server = try createServer(allocator, config);
    defer server.deinit();

    std.log.info("Zig HTTP Server listening on {s}:{d}", .{ config.host, config.port });

    // 接受连接主循环
    while (true) {
        const connection = try server.accept();
        try handleConnection(connection);
    }
}

3.2 HTTP 请求解析

const HttpMethod = enum { GET, POST, PUT, DELETE, PATCH };

const HttpRequest = struct {
    method: HttpMethod,
    path: []const u8,
    version: []const u8,
    headers: std.StringHashMap([]const u8),
    body: []const u8,
};

fn parseRequest(allocator: std.mem.Allocator, data: []const u8) !HttpRequest {
    var lines = std.mem.split(u8, data, "\r\n");
    const request_line = lines.first();

    // 解析请求行:GET /path HTTP/1.1
    var parts = std.mem.split(u8, request_line, " ");
    const method_str = parts.first();
    const path = parts.rest();

    const method: HttpMethod = blk: {
        if (std.mem.eql(u8, method_str, "GET")) break :blk .GET;
        if (std.mem.eql(u8, method_str, "POST")) break :blk .POST;
        if (std.mem.eql(u8, method_str, "PUT")) break :blk .PUT;
        if (std.mem.eql(u8, method_str, "DELETE")) break :blk .DELETE;
        if (std.mem.eql(u8, method_str, "PATCH")) break :blk .PATCH;
        return error.InvalidMethod;
    };

    // 解析请求行中的 HTTP 版本
    const last_space = std.mem.lastIndexOf(u8, path, " ") orelse return error.InvalidRequest;
    const version = path[last_space + 1..];
    const clean_path = path[0..last_space];

    // 解析头部
    var headers = std.StringHashMap([]const u8).init(allocator);
    var body_start_idx: ?usize = null;
    var idx: usize = 0;

    while (lines.next()) |line| : (idx += 1) {
        if (line.len == 0) {
            // 空行表示 body 开始
            body_start_idx = idx;
            break;
        }
        if (std.mem.indexOf(u8, line, ":")) |colon_idx| {
            const key = std.mem.trim(u8, line[0..colon_idx], " ");
            const value = std.mem.trim(u8, line[colon_idx + 1..], " ");
            try headers.put(key, value);
        }
    }

    const body = if (body_start_idx) |start| blk: {
        const body_lines = std.mem.split(u8, data, "\r\n");
        var i: usize = 0;
        var body_content: []const u8 = "";
        while (body_lines.next()) |l| : (i += 1) {
            if (i >= start) {
                body_content = l;
                break;
            }
        }
        break :blk body_content;
    } else "";

    return HttpRequest{
        .method = method,
        .path = clean_path,
        .version = version,
        .headers = headers,
        .body = body,
    };
}

3.3 路由系统与响应处理

const HttpResponse = struct {
    status_code: u16,
    reason: []const u8,
    headers: std.StringHashMap([]const u8),
    body: []const u8,
};

fn createResponse(status_code: u16, body: []const u8) HttpResponse {
    const reason = switch (status_code) {
        200 => "OK",
        201 => "Created",
        204 => "No Content",
        400 => "Bad Request",
        404 => "Not Found",
        405 => "Method Not Allowed",
        500 => "Internal Server Error",
        else => "Unknown",
    };

    var headers = std.StringHashMap([]const u8).init(std.heap.page_allocator);
    headers.put("Content-Type", "text/plain; charset=utf-8") catch unreachable;
    headers.put("Content-Length", std.fmt.allocPrint(
        std.heap.page_allocator,
        "{d}",
        .{body.len}
    ) catch unreachable) catch unreachable;

    return HttpResponse{
        .status_code = status_code,
        .reason = reason,
        .headers = headers,
        .body = body,
    };
}

fn serializeResponse(response: HttpResponse) ![]u8 {
    var buf = std.ArrayList(u8).init(std.heap.page_allocator);
    const writer = buf.writer();

    try writer.print("HTTP/1.1 {d} {s}\r\n", .{ response.status_code, response.reason });

    var it = response.headers.iterator();
    while (it.next()) |entry| {
        try writer.print("{s}: {s}\r\n", .{ entry.key_ptr.*, entry.value_ptr.* });
    }

    try writer.print("\r\n", .{});
    try writer.write(response.body);

    return buf.toOwnedSlice();
}

// 路由表
const Route = struct {
    method: HttpMethod,
    path: []const u8,
    handler: *const fn (req: HttpRequest) HttpResponse,
};

fn handleRequest(req: HttpRequest) HttpResponse {
    // 路由匹配
    if (req.method == .GET and std.mem.eql(u8, req.path, "/")) {
        return createResponse(200, "Zig HTTP Server is running!");
    }

    if (req.method == .GET and std.mem.eql(u8, req.path, "/health")) {
        return createResponse(200, `{"status":"ok","uptime":${uptime}}`);
    }

    if (req.method == .GET and std.mem.startsWith(u8, req.path, "/hello/")) {
        const name = req.path[7..];
        const greeting = std.fmt.allocPrint(
            std.heap.page_allocator,
            "Hello, {s}!",
            .{name}
        ) catch return createResponse(500, "Internal Server Error");
        const response = createResponse(200, greeting);
        return response;
    }

    if (req.method == .POST and std.mem.eql(u8, req.path, "/echo")) {
        return createResponse(200, req.body);
    }

    return createResponse(404, "404 Not Found");
}

3.4 连接处理与性能分析

fn handleConnection(connection: net.StreamServer.Connection) !void {
    defer connection.stream.close();

    var buf: [8192]u8 = undefined;
    const bytes_read = try connection.stream.read(&buf);

    if (bytes_read == 0) return;

    const request_data = buf[0..bytes_read];

    const request = parseRequest(std.heap.page_allocator, request_data) catch |err| {
        const response = createResponse(400, "Bad Request");
        const serialized = try serializeResponse(response);
        connection.stream.write(serialized) catch return;
        return;
    };
    defer request.headers.deinit();

    const response = handleRequest(request);
    const serialized = try serializeResponse(response);
    defer std.heap.page_allocator.free(serialized);

    try connection.stream.writeAll(serialized);
}

性能分析

  1. 零动态分配(请求解析路径):请求解析完全在栈上进行,使用固定大小的 buffer。只有在需要时才动态分配(比如查询参数字符串)。
  2. 无垃圾回收:所有内存都是显式管理的,不存在 GC 暂停。
  3. 直接系统调用:Zig 标准库直接映射到 POSIX 系统调用,没有额外的运行时抽象层。

四、横向对比:谁才是真正的 C 继承者

4.1 Zig vs. Rust:两条路线的哲学分歧

维度RustZig
内存安全编译时借用检查器程序员负责 + comptime 辅助
学习曲线陡峭(lifetime、所有权)平缓(语法接近 C)
编译速度较慢(Cargo 增量编译)极快(与 C 项目相当)
运行时无标准运行时无运行时(零开销)
泛型泛型 + traitcomptime + 匿名结构体
错误处理Result<T, E> + ?!T 联合类型
目标用户追求内存安全的系统编程追求简洁性和控制力的系统编程

Rust 的哲学是「让不安全的代码变得困难」——通过编译器的强制检查,让内存安全问题在编译时暴露。代价是学习曲线陡峭。

Zig 的哲学是「程序员知道自己在做什么」——编译器不替你做安全检查,但你也不需要跟编译器「斗争」。代价是代码质量完全依赖程序员。

4.2 Zig vs. Go:简单性的两种诠释

Go 的简单性来自于「语言层面只提供最基础的,要什么自己组合」。但 Go 的 simple 和 Zig 的 simple 是不同的:

  • Go 的简单性是社区约束——通过代码风格指南和 gofmt 来统一代码形态。
  • Zig 的简单性是设计约束——语言本身不提供高级抽象,让程序员自己决定抽象层次。

举个例子,并发。Go 用 goroutine + channel 提供了一套优雅的并发模型。Zig 则把异步完全交给了标准库,语言层面不提供协程关键字——你用的是标准库的 event loop + thread pool:

// Zig 的异步:标准库层面,不是语言层面
const async_io = struct {
    fn readFile(path: []const u8, allocator: std.mem.Allocator) ![]u8 {
        // 使用 std 的异步 IO
        // 语言不需要知道「async」是什么
    }
};

五、Bun AI 重写事件:Andrew Kelley 炮轰了什么

5.1 事件始末

2026年8月,Bun 的创始人 Jarred Sumner 在社交媒体上宣布,他用一批并行运行的 Claude 实例重写了 Bun 的核心代码。据说整个重写只用了几天时间。

消息一出,技术社区炸了。有人欢呼——「看,AI 编程已经到达这个高度了!」也有人担忧——「没有人审查的 AI 生成代码,真的能用在生产环境吗?」

Andrew Kelley 的回应毫不客气:

"这段代码没有人把关。这不是编程,这是用 AI 生成大量没人读的烂代码。"

5.2 代码质量的本质问题

Kelley 的批评触及了一个核心问题:代码不只是给机器执行的指令,还是给人看的文档

Bun 的 JavaScript 运行时每秒处理数百万请求,出现 bug 的代价是:

  • 应用程序崩溃
  • 内存泄漏
  • 安全漏洞

这些后果不会因为「代码是 AI 写的」就消失。而 AI 生成的代码有一个普遍问题:它倾向于生成「看起来对」的代码,而不是「真正对」的代码。区别在于边界条件、异常处理、以及对硬件特性的正确假设。

5.3 Zig 的立场:质量比速度重要

Andrew Kelley 在多个场合表达过类似的观点:

"我们宁愿 Zig 1.0 发布时只有 2000 行代码,但要保证每一行都是正确的,也不愿意发布时有 20000 行代码但充满隐患。"

这不是技术上的保守,这是对软件的本质理解:软件的价值不在于功能多,而在于可靠


六、Zig 在 2026 年的生态图景

6.1 值得关注的里程碑

  1. CachyOS 8月更新(2026年8月):将图形包管理器 Shelly 从 C# 重构为 Zig,这是 Zig 在真实 Linux 发行版中的首个重量级应用。
  2. Zig 单片机教程(2026年8月):面向嵌入式开发者的 Zig 实战教程正式上线,覆盖从环境搭建到 SPI 通信的完整路径。
  3. Netcode.io:用 Zig 实现 QUIC、HTTP/3 和 WebTransport 的开源项目,目标是为 Zig 提供企业级网络协议栈。

6.2 生态现状

Zig 的生态还在成长中,与 Rust 相比差距明显:

  • 没有官方的包管理器(zigmod 社区维护)
  • 标准库不如 Rust std 完善
  • 第三方库数量有限
  • IDE 支持(Zig Language Server)在完善中但仍有坑

但反过来想——Zig 的标准库几乎所有核心功能都是手写的:

  • 手写的 TLS 库(无外部依赖)
  • 手写的 HTTP 服务器
  • 手写的压缩库
  • 手写的 SQLite 绑定

这正是「少即是多」哲学的体现:不需要 500 个 crate 来构建一个 HTTP 服务器

6.3 Zig 的短板:async 困境

必须承认,Zig 目前在异步编程方面有明显的短板。标准库的 async/await 实现不完整,文档也有不少坑。比如:

// 这段代码在某些 Zig 版本中会触发编译器 bug
test "async allocator" {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    // 异步内存分配的交互目前存在未解决的边界问题
    // 建议在 0.14 稳定版之前,回退到同步分配模式
}

如果你在 2026 年要用 Zig 写生产级的异步服务,建议仔细评估标准库 async 的成熟度,或者等待 0.15 版本的改进。


七、实战:用 Zig 构建命令行工具

7.1 项目初始化

mkdir zig-cli-tool
cd zig-cli-tool
zig init-exe

7.2 完整的 CLI 工具

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

pub const ToolError = error {
    InvalidArgument,
    FileNotFound,
    ParseError,
};

pub const Config = struct {
    verbose: bool = false,
    input_file: ?[]const u8 = null,
    output_file: ?[]const u8 = null,
    mode: enum { upper, lower, count } = .lower,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(!gpa.deinit());
    const allocator = gpa.allocator();

    // 解析命令行参数
    var config = Config{};

    const args_list = try std.process.argsAlloc(allocator);
    defer std.heap.page_allocator.free(args_list);

    var i: usize = 1; // 跳过程序名
    while (i < args_list.len) : (i += 1) {
        const arg = args_list[i];

        if (std.mem.eql(u8, arg, "-v") or std.mem.eql(u8, arg, "--verbose")) {
            config.verbose = true;
        } else if (std.mem.eql(u8, arg, "-o") or std.mem.eql(u8, arg, "--output")) {
            i += 1;
            if (i >= args_list.len) {
                std.log.err("Option {s} requires an argument", .{arg});
                return error.InvalidArgument;
            }
            config.output_file = args_list[i];
        } else if (std.mem.eql(u8, arg, "-m")) {
            i += 1;
            if (i >= args_list.len) {
                std.log.err("Option {s} requires an argument", .{arg});
                return error.InvalidArgument;
            }
            const mode_arg = args_list[i];
            if (std.mem.eql(u8, mode_arg, "upper")) {
                config.mode = .upper;
            } else if (std.mem.eql(u8, mode_arg, "lower")) {
                config.mode = .lower;
            } else if (std.mem.eql(u8, mode_arg, "count")) {
                config.mode = .count;
            } else {
                std.log.err("Unknown mode: {s}", .{mode_arg});
                return error.InvalidArgument;
            }
        } else if (!std.mem.startsWith(u8, arg, "-")) {
            config.input_file = arg;
        }
    }

    if (config.verbose) {
        std.log.info("Config: {any}", .{config});
    }

    // 读取输入
    var input_data: []u8 = undefined;
    if (config.input_file) |path| {
        input_data = try std.fs.cwd().readFileAlloc(
            allocator,
            path,
            1024 * 1024 // 最大 1MB
        );
        defer allocator.free(input_data);
    } else {
        // 从 stdin 读取
        const stdin = std.io.getStdIn();
        input_data = try stdin.readToEndAlloc(allocator, 1024 * 1024);
        defer allocator.free(input_data);
    }

    // 处理
    var output: []u8 = switch (config.mode) {
        .upper => blk: {
            var result = try allocator.alloc(u8, input_data.len);
            for (input_data, 0..) |c, j| {
                result[j] = std.ascii.toUpper(c);
            }
            break :blk result;
        },
        .lower => blk: {
            var result = try allocator.alloc(u8, input_data.len);
            for (input_data, 0..) |c, j| {
                result[j] = std.ascii.toLower(c);
            }
            break :blk result;
        },
        .count => blk: {
            const result = try std.fmt.allocPrint(
                allocator,
                "{d}\n",
                .{input_data.len}
            );
            break :blk result;
        },
    };
    defer allocator.free(output);

    // 写入输出
    if (config.output_file) |path| {
        try std.fs.cwd().writeFile(path, output);
    } else {
        const stdout = std.io.getStdOut();
        try stdout.writeAll(output);
    }

    if (config.verbose) {
        std.log.info("Processed {d} bytes", .{input_data.len});
    }
}

7.3 构建与运行

# 调试构建
zig build

# Release 优化构建(接近 C 的性能)
zig build -Drelease-safe   # 保留安全检查
zig build -Drelease-fast   # 极致优化,禁用安全检查
zig build -Drelease-small  # 优化体积

# 运行
./zig-out/bin/zig-cli-tool -m upper -o output.txt input.txt
./zig-cli-tool -m count < input.txt

二进制体积对比(Release-fast 模式):

语言工具二进制大小
Zig上例工具~180KB
Go等效工具~2.5MB
Rust等效工具~1.2MB

Zig 的二进制大小优势来自它没有运行时和 libc 的完全静态链接。使用 musl libc 的话,二进制可以进一步压缩到 ~100KB。


八、给程序员的选择建议

8.1 什么时候选 Zig

  • 写 C++/C 项目,想提高可维护性但不想换生态
  • 构建对二进制大小敏感的工具(嵌入式、CLI 工具)
  • 需要直接与硬件交互(Zig 的 pointer 和 volatile 控制非常精细)
  • 重视编译速度(Zig 的增量编译比 Rust 快一个量级)
  • 想写一个从头到脚都是自己掌控的系统

8.2 什么时候不用 Zig

  • 需要成熟生态(Rust 的 crate 生态是 Zig 难以望其项背的)
  • 需要异步高性能网络服务(Zig stdlib async 目前不稳定)
  • 团队中没有 Zig 经验的开发者(学习曲线虽然比 Rust 平缓,但仍需要投入)
  • 项目需要长期维护(Zig 的 breaking changes 频率较高)

结语:好代码是时间的函数

回顾 Andrew Kelley 和 Zig 这十年,最让我触动的不只是技术本身,而是背后那种「不向速度妥协」的精神。

在 AI 批量生产代码、Bun 用 Claude 几天重写整个项目的时代,Zig 坚持用十年打磨一个编译器。这种做法在商业世界里看起来很「傻」——竞争对手三个月迭代一次,你一年才发一个版本。

但程序员茄子(chenxutan.com)的读者应该最懂这个道理:代码质量是时间的函数。你花多少时间思考、审视、打磨,决定了这段代码能在多长的时间尺度内保持正确。

Rust 的所有权系统花了 15 年设计,实现了内存安全。Zig 的 comptime 机制花了 10 年打磨,正在重新定义「元编程」应该是什么样子。Andrew Kelley 拒绝发布 1.0,不是因为做不出来,而是因为:

「当我说 1.0 的时候,我指的是它已经准备好了。准备好,意味着没有任何我无法接受的问题。」

这不是傲慢,这是对自己作品的基本尊重。

下一个十年,Zig 能不能成为「未来 50 年的系统编程语言」?我不知道。但我知道,只要 Andrew Kelley 还在,Zig 就不会变成第二个 JavaScript——不会被功能蔓延和向后兼容的锁链拖垮。

对于程序员来说,这意味着:学 Zig,不只是学一门新语言,还是学一种做软件的方式


Tags: Zig|Andrew Kelley|系统编程|comptime|元编程|WebAssembly|编程语言哲学|开源

推荐文章

PHP 如何输出带微秒的时间
2024-11-18 01:58:41 +0800 CST
使用xshell上传和下载文件
2024-11-18 12:55:11 +0800 CST
程序员茄子在线接单