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 的工匠精神。
这不只是一个技术路线之争。这背后是整个行业对「什么是好代码」「软件工程应该如何做」的根本性分歧。
本文将深入拆解:
- Zig 的核心设计哲学——为什么它要「少即是多」
- Zig 的编译模型和 comptime 机制——元编程的正确打开方式
- 与 C/Rust/Go 的横向对比——谁才是真正的 C 继承者
- Andrew Kelley 十年反思的深层含义
- Bun AI 重写事件的工程视角解读
- Zig 在 2026 年的生产环境现状
- 一段 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 var 和 comptime 函数的用法。在 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);
}
性能分析:
- 零动态分配(请求解析路径):请求解析完全在栈上进行,使用固定大小的 buffer。只有在需要时才动态分配(比如查询参数字符串)。
- 无垃圾回收:所有内存都是显式管理的,不存在 GC 暂停。
- 直接系统调用:Zig 标准库直接映射到 POSIX 系统调用,没有额外的运行时抽象层。
四、横向对比:谁才是真正的 C 继承者
4.1 Zig vs. Rust:两条路线的哲学分歧
| 维度 | Rust | Zig |
|---|---|---|
| 内存安全 | 编译时借用检查器 | 程序员负责 + comptime 辅助 |
| 学习曲线 | 陡峭(lifetime、所有权) | 平缓(语法接近 C) |
| 编译速度 | 较慢(Cargo 增量编译) | 极快(与 C 项目相当) |
| 运行时 | 无标准运行时 | 无运行时(零开销) |
| 泛型 | 泛型 + trait | comptime + 匿名结构体 |
| 错误处理 | 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 值得关注的里程碑
- CachyOS 8月更新(2026年8月):将图形包管理器 Shelly 从 C# 重构为 Zig,这是 Zig 在真实 Linux 发行版中的首个重量级应用。
- Zig 单片机教程(2026年8月):面向嵌入式开发者的 Zig 实战教程正式上线,覆盖从环境搭建到 SPI 通信的完整路径。
- 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|编程语言哲学|开源