Zig发展轶事(202x)
本文主要用于记录Zig编程语言发展和使用过程中的大事记,截止到2030年前。HashiCorp 创始人向 Zig 软件基金会捐赠 30 万美元
宣布从 GitHub 迁移至 Codeberg
全面禁止 LLM 辅助贡献:押注于人而非代码
一人造编程语言,10年不发1.0,还把AI和GitHub都禁了
打响了反LLM的第一枪,也许不是最后一个
凭什么让 C 开发者集体兴奋
Mitchell Hashimoto 向 Zig 软件基金会捐赠 30 万美元
Zig 软件基金会 (ZSF) 于2024年10月上旬宣布获得了来自 HashiCorp 联合创始人兼首席执行官 Mitchell Hashimoto 与其妻子的 30 万美元承诺捐款,以个人名义捐赠。捐款将分成两笔 15 万美元的付款,第一笔款项已经完成转账,另一笔计划一年后支付。ZSF 在公告中明确阐述了他们的使命以及这笔钱的具体用途:
在过去的几年中,Zig 项目的采用率和贡献者数量都得到了显着增长,以至于开发活动的数量已经超过了我们获得的捐款数量。我们目前有越来越多的 PR 需要审查,并且有一批有资格完成这项工作的核心贡献者,但没有足够的资金来支付计费时间,无法完全满足需求。就在上周,为了控制日益增长的云基础设施成本,我们放弃了 AWS,甚至在此之前,我们 90% 以上的收入一直用于支付贡献者的时间。这不是理所当然的事情,因为非营利组织很容易产生不成比例的运营开销。
因此,ZSF 计划使用这笔资金继续为核心捐助者提供就业机会:譬如为 Jacob Young 提供全职工作,“他是 C 语言后端、x86 后端、增加了 Zig 支持的 LLDB fork 的主要作者,同时还负责维护 eZ80 工具链。”
除了对 Mitchell 表达感谢外,ZSF 还感谢了 Bun 和 TigerBeetle 对 ZSF 和 ZigTools(支持 ZLS 开发的社区计划)的财政支持,以及在今年早些时候成为 ZSF 的赞助商的 ZML。Mitchell 也在个人博客中阐述了自己支持 ZSF 的原因,并呼吁更多人为 ZFS 进行捐赠:
我从 2019 年的某个时候就开始关注 Zig 项目。我在 2021 年公开分享了我对该项目的兴奋之情。那年晚些时候我开始使用 Zig,到 2022 年初,我开始 撰写有关 Zig 的文章并 为编译器做贡献,从那时起,我继续做了几十个代码贡献。2023 年我公开分享了我的终端项目 Ghostty,它是用 Zig 编写的。现如今我大部分的编码时间都花在了 Zig 上。
我的家人喜欢支持我们所信仰的事业。作为其中的一部分,我们希望支持那些我们认为可以带来变革和影响的独立软件项目,这既是回馈给予我如此之多的社区的一种方式,更重要的是,这也是彰显和鼓励为热爱而建设的文化的一种方式。Zig 就是其中一个项目。
Zig 从一开始就是一个充满激情的项目,并且一直如此。它管理良好,社区强大,融资模式透明且可持续。作为一项技术,它雄心勃勃且富有创新性,但仍然实用且务实。它要达到稳定和更广泛的行业采用还有很长的路要走,但我相信它有一条清晰的道路和机会来实现这一目标。
明显 Bug 拖三年不修,Zig 宣布从 GitHub 迁移至 Codeberg
因对 GitHub 持续恶化的服务质量失望,Zig 软件基金会于2025年12月宣布将项目迁移至非营利代码托管服务 Codeberg。据悉事件起源于一个名为 "safe_sleep.sh 脚本无限期挂起" 的 bug。2022 年 2 月,GitHub 将 posix "sleep" 命令替换为 "safe_sleep" 脚本,但该脚本存在明显缺陷 —— 如果进程未在 1 秒间隔内被调度运行,脚本就会陷入死循环,持续占用 100% CPU。
Zig 核心开发者 Matthew Lugg 在 2025 年 4 月的错误报告中指出:" 在负载极高的 CI 服务器上,这种情况很容易发生。一旦发生,后果非常严重:它会彻底摧毁一个运行器,直到人工干预。在 Zig 的 CI 运行器服务器上,我们观察到多个这样的进程已经运行了数百小时,悄无声息地导致两个运行器服务器宕机数周。"
尽管该问题在 2025 年 4 月被正式报告,GitHub 直到 8 月 20 日才合并修复代码,且从未在原讨论帖中回应,该帖直到 12 月 1 日才被关闭。更讽刺的是,修复方案早在 2024 年 2 月就已提出,但在一年多时间里未经审查,还曾在 2025 年 3 月被 GitHub 机器人自动关闭。
Zig 软件基金会主席兼首席开发者 Andrew Kelly 在宣布迁移时称:"GitHub Actions 存在不可原谅的漏洞,却完全被忽视。GitHub 的 CEO 曾说过 ' 要么拥抱 AI,要么滚蛋 ',看来微软的那些走狗们领会了其中的含义,因为 GitHub Actions 开始 ' 随机调度 '—— 看似随机地选择要运行的任务。再加上其他漏洞以及无法手动干预,这导致我们的持续集成系统严重积压,甚至连主分支的提交都无法检查。"
Kelly 随后为这篇 "煽动性帖子" 道歉,但 Zig 基金会的迁移决定并未改变。
Answer.AI 和 Fast.AI 联合创始人 Jeremy Howard 在社交媒体上表示:"这个漏洞的实现方式非常明显,几乎任何人一眼就能看出它会一直占用 100% CPU,并且除非任务恰好在正确的时间检查时间,否则它会一直运行下去。我实在无法理解,这样一系列令人瞠目结舌、匪夷所思的事件是如何在一个正常运转的组织中产生的。"
Zig 并非唯一离开 GitHub 的项目。Dillo 浏览器项目创建者 Rodrigo Arias Mallo 上周末也宣布计划离开 GitHub,理由包括过度依赖 JavaScript、可用性下降、审核工具不足,以及 "过度关注 LLM 和生成式 AI,这些正在摧毁开放网络"。
自2025年1月以来,非营利代码托管平台 Codeberg 的支持会员人数已从 600 多人翻倍至上周的 1200 多人。相比之下,GitHub 尚未透露当前付费用户总数。微软 CEO 萨蒂亚・纳德拉在 2025 年第三季度财报会上称 "GitHub Copilot 用户超过 1500 万,同比增长超过 4 倍",但未说明有多少用户为 Copilot 或其他服务付费。2024 年第四季度,GitHub 年收入运行率为 20 亿美元,其中 GitHub Copilot 订阅收入约占年增长的 40%。
全面禁止 LLM 辅助贡献:押注于人而非代码
2026年4月下旬,知名开发者 Simon Willison 在其博客发文,详细解读了 Zig 编程语言项目近期出台的严格反 AI 贡献政策(可查看Zig 项目官方政策)。这项政策明确禁止在所有 issue、Pull Request 及评论中使用 LLM 辅助生成内容,引发了开源社区对 "AI 与开源治理" 关系的广泛讨论。
Zig 项目核心维护者的立场非常鲜明:开源项目的终极目标不是获取代码,而是培养长期可信的贡献者。用他们自己的话说,"you play the person, not the cards"(你押注的是人,而非手中的牌)。审查 Pull Request 的首要目的从来不是合并代码本身,而是帮助新贡献者成长,让他们理解项目规范、建立信任关系。一旦引入 LLM 辅助,这个培养过程就被架空了 —— 维护者无法判断提交者是否真正理解自己的代码。
试问如果一个 PR 主要由 LLM 编写,那么项目维护者为什么要花时间审查和讨论这个 PR,而不是启动自己的 LLM 来解决同样的问题呢?
这一观点得到了颇具说服力的现实佐证。Bun(基于 Zig 构建的高性能 JavaScript 运行时)已被 Anthropic 收购,团队内部重度使用 AI 辅助开发。然而即便如此,Bun 团队仍然无法向 Zig 上游提交 AI 辅助生成的优化代码,因为其根本不符合 Zig 项目对 "真实人类贡献者" 的要求。
Zig 项目的逻辑并非排斥技术进步,而是对开源社区长期健康的审慎考量。当 AI 可以瞬间生成看似合格的代码时,项目维护者面临的最大风险不再是代码质量,而是无法识别 "谁在真正学习、谁只是让 AI 代笔"。这种信息不对称会瓦解 mentorship(导师制)这一开源社区最核心的传承机制。
一人造编程语言,10年不发1.0,还把AI和GitHub都禁了
Andrew Kelley在2018年辞掉了OkCupid的工作。不是跳槽。是开始靠捐款活着。他要做一门编程语言叫Zig。八年之后,JetBrains请他上播客。30万人看了这期采访。Stack Overflow把他的语言评为第四最受仰慕的编程语言。Uber在用,Bun在用,连Anthropic——就是做Claude的那家公司——也在用。
但版本号还是0.16,他禁了AI写代码,还把项目从GitHub搬走了。他说自己"基本上没法被雇佣",他到底在想什么?
他不是因为热爱编程才造语言的
Kelley最初想做一个数字音频工作站——就是音乐人用来录音、混音的软件。
写这种东西,你得跟C语言库打交道。底层驱动、音频处理、实时计算,全是C的地盘。
他试了Go。垃圾回收会在音频处理时产生延迟。
做音乐的人知道,哪怕几毫秒的延迟都能毁掉一整段录音。
他试了C++。一个小错误导致内存损坏,他查了三个星期。
他试了Rust。花了一个月,就为了写一个字体渲染功能——Rust的规则太严,他写不出来。
每个语言都不行。他的解决方案不是"忍忍就算了"。是自己写一个。
这种性格贯穿了他后面所有的决定。不满意就不接受。改不了别人就自己来。
10年了,版本号才0.16。最多人问他的问题就是这个:为什么还不发1.0?
他的回答是:"1.0是一个承诺。一旦发了,你就锁死了。如果锁死的是错误的设计,你要带着它活50年。"
他经常提C语言。C之所以成功,不是因为先进。是因为几十年几乎不变。稳定本身就是最大的竞争力。
Kelley要的就是这个。他的目标不是一个能用十年的语言。是五十年。所以他不急。
他的基金会一年收入67万美元。他自己的年薪15.4万。不是风投,是社区捐的。
有人拿了1亿美元去做编程语言。失败了。
我宁愿带5个人慢慢走,也不要被钱裹挟着仓促锁死一个错误的设计。
这句话背后是一个很执拗的信念:一次做对比回头改要便宜得多。离谱的是,它好像在奏效。
2026年3月,有人调查了112个主要开源项目。只有4个完全禁止AI代码贡献。
Kelley的Zig是其中之一。禁令写在行为准则里,不是建议,是硬性规定。
AI生成的代码不让提,LLM写的注释不让提,甚至用AI工具brainstorm过的方案也不行。
他在采访里说了一句很绝的话:"AI辅助的贡献不是零价值,是负价值。"
为什么是负的?
Zig有200个待审核的Pull Request,审代码的核心成员就那么几个。
AI把提PR的门槛降到了零。以前你得读懂代码才能提交补丁。现在让AI生成一个"看着差不多"的,30秒就提交了。
结果:核心成员的时间全花在审垃圾上了。真正花时间写代码的新人,他们的贡献被挤到了后面。麻了。
但这还不是最关键的原因。最关键的是:Kelley不要代码。他要的是人。
Zig有个东西叫"导师制"。新人提交了不完美的代码,核心成员会一行一行告诉他哪里可以改、为什么这么改。慢慢地,新人变成了可靠的贡献者。
AI提交的代码背后没有人。你给它写review,它什么都没学到。下次还用AI生成,还是垃圾。
Kelley管这叫"贡献者扑克"——用最小代价博弈最大回报。他不要赌徒。他要的是学徒。
2025年11月,Kelley做了一件在开源圈很少见的事:把项目从GitHub搬走了。
GitHub是全球最大的代码托管平台。几乎所有主流开源项目都在上面。在上面意味着被看见。
他搬到了Codeberg——一个德国非营利组织运营的小平台。
原因他写了一篇很长的公告,三个:
GitHub Actions坏了。
他说GitHub的CI系统开始"随机调度"——不是每个提交都能触发检查。master分支的代码,有时候没人验。
他发明了一个词叫"vibe-scheduling",讽刺"vibe coding"。我们在这里写软件。如果CI不工作,我们就换一个能工作的。
GitHub在推AI。
界面上到处是"用Copilot提交issue"的按钮。用户被引导着提交AI生成的内容。这直接违反了Zig的AI禁令。
他不信任微软。
GitHub是微软的,被收购7年了。他提到了GitHub跟ICE(美国移民和海关执法局)的合作关系。
最狠的是他对GitHub Sponsors的态度。那是Zig基金会重要的捐款来源。他说:我们认为这是个"负债"。
宁可少拿钱,也不留在不信任的平台上。
采访快结束的时候,主持人问了一个看起来很随意的问题:如果你不干Zig了,你回去上班吗?
Kelley的回答:"我基本上没法被雇佣。因为我拒绝让AI写代码,而现在的公司都要求用AI。"
他写代码用终端和Vim。不用任何闭源工具。这句话是他坐在JetBrains的播客椅子上说的——JetBrains就是做闭源IDE的。
"用自己的电脑、交着自己的电费、还要按月付费去用别人电脑上的闭源工具。对我来说这是疯了。"
Hacker News上有人叫他堂吉诃德。有人说他是这个时代少有的还能守住底线的人。他自己怎么看?
"我总是听到人说AI代码效果出人意料地好。但我对软件的标准不是'出人意料地好'。是毫不妥协的完美。"
2026年,Kelley 37岁,未婚;他在采访里说过,除了编程,他最大的爱好是徒步。
他住在阿姆斯特丹。年薪15.4万。可以更高但他不要。
他的语言被AI公司用来构建AI产品。他自己的项目禁止任何AI参与。他把这个叫"Zig的AI悖论"。
他们选择Zig不是因为它跟AI有关。是因为真的好用。
67万美元的年预算。5个人的团队。每个贡献者他都亲手带。
在一个所有人都喊着"用AI提效10倍"的时代,他在用最慢的方式做软件。
慢到10年不出1.0。慢到亲手review每一个PR。慢到用导师制培养每一个新人。
他自己说过一句话,算是个回答:"Zig是对计算机的一座神殿。"
一个人,花了十年,给计算机盖庙。不管你觉得他是疯子还是圣人,你得承认:这个时代,敢这么做的人不多了。
打响了反LLM的第一枪,也许不是最后一个
Zig是一种通用编程语言和工具链,用于维护健壮、高效且可重用的软件。专注于调试应用程序,而不是调试编程语言知识。没有隐藏的控制流。没有隐藏的内存分配。没有预处理器,没有宏。逐步改善你的 C/C++/Zig 代码库。将 Zig 作为一个零依赖的,支持开箱即用交叉编译的 C/C++ 编译器。
用 Zig 编写的最知名项目可能要数 Bun JavaScript 运行时,它于 2025 年 12 月被 Anthropic 收购,并且不出所料地大量使用了 AI 辅助。Zig是在所有主要开源项目中拥有最为严格的反 LLM 政策之一:
1. 不允许在 issue 中使用 LLM。
2. 不允许在 pull request 中使用 LLM。
3. 不允许在 bug 追踪器的评论中使用 LLM,包括翻译。鼓励使用英语,但不做强制要求。欢迎用你的母语发帖,其他人可以使用他们自己喜欢的翻译工具来理解你的话。
4. 禁止使用 LLM 进行头脑风暴并分享结果,即使你撰写了文字也不行。如果你使用聊天机器人就问题跟踪器上的评论提供建议,那么该评论是不受欢迎的。
在《贡献者扑克与 Zig 的 AI 禁令》(via Lobste.rs)一文中,Zig 软件基金会社区副总裁 Loris Cro 于2026年6月解释了这一严格禁令的理由。这是迄今为止我看到的对全面禁止 LLM 辅助贡献最清晰的阐述:在成功的开源项目中,你最终会达到一个阶段——收到的 PR 数量超出了你的处理能力。鉴于我前面提到的这些情况,按理说应该停止接受不完美的 PR,以最大化你的工作投入回报,但这并不是 Zig 项目的做法。相反,我们尽最大努力帮助新贡献者将他们的工作合并进去,即使他们需要一些帮助才能做到。我们这样做不仅仅因为这是"正确"的事,还因为这是明智的事。
Zig 更看重贡献者而非其贡献。每位贡献者都代表着 Zig 核心团队的一笔投资——审查和接受 PR 的首要目标不是合入新代码,而是帮助培养新的贡献者,使其随着时间推移变得值得信赖且高产。也就是Zig开源项目更看重的是开源作者本身随着时间推移逐渐成长,对项目逐渐理解的过程中成为开源的核心资产。
LLM 辅助彻底打破了这一点。即便 LLM 帮你向 Zig 提交了一份完美的 PR 也无济于事——Zig 团队花在审查你工作上的时间,对他们为整个项目培养新的、自信的、值得信赖的贡献者毫无帮助,直白点说就是断了后路;
Loris 解释了这个名字“贡献者扑克”的由来:我之所以称之为"贡献者扑克",是因为就像人们谈到真正的扑克牌游戏时说的那样——"你打的是人,不是牌"。在贡献者扑克中,你押注的是贡献者,而不是他们第一个 PR 的内容。
这对我来说很有道理。它也关联了一个我在其他地方看到流传的观点:如果一个 PR 主要是由 LLM 编写的,项目维护者为什么要花时间审查和讨论这个 PR,而不是自己启动 LLM 来解决同样的问题呢?好吧,虽然这么说,但是确实已经成为主流了,LLM编写,LLM审核,合并。
附录:贡献者扑克与 Zig 的 AI 禁令
在 Zig 软件基金会工作期间,我有机会学习到许多关于软件的有趣知识。今天我想分享的是,对于任何规模足以吸引贡献者的开源项目而言,理解软件运作机制都至关重要的一点。开源开发有利有弊,你需要利用其带来的好处来弥补其带来的弊端,而弊端确实很多。
首先,开源与许多商业模式不兼容,它的核心理念是必须免费提供有价值的东西。当然,作为免费提供东西的回报,你也会免费获得一些东西,主要以代码贡献的形式体现。
不幸的是,这些贡献(以后简称它们为PR )不仅不能带来收入,它们本身往往还会增加额外的工作量和摩擦。事实上,维护者直接实现某个更改通常比与 PR 作者合作使其代码达到可合并状态要省力得多。
根据我前面提到的,开源开发看起来似乎很糟糕,但我坚信它有潜力以远低于大多数其他开发模式的成本,生产出更高质量的产品;你只需要精通开源之道。我之前已经就此主题写过文章,但今天我将重点讨论一个具体方面:贡献者扑克。
参与开源项目是一个迭代的过程,贡献者能为项目带来的价值大多体现在后期的迭代中。换句话说,你最初投入一些精力(相当于下注)来引导一位新贡献者,然后你希望随着贡献者变得更加值得信赖和贡献更多,这种关系最终会给你带来回报。
对这种动态的清晰理解,随着时间的推移,为 Zig 项目带来了巨大的价值。从零开始构建编译器工具链是一项极其庞大的工程,如果没有贡献者的鼎力相助,几乎不可能完成。
在 Zig 的早期,可以对每个新贡献者进行投资,但现在该项目已经发展到这样的程度:涌入的 PR 数量远远超过了核心贡献者可用于玩“贡献者扑克”的精力。实际上,这意味着有些优秀的 PR 长时间无人审核,这可能会导致有价值的贡献者对为 Zig 做贡献失去兴趣。
我们在年度财务报告中提到过这个问题,更重要的是,我们积极意识到这个问题,并希望将来能够解决(或至少减轻)这个问题。
不幸的是,这不仅是一个本身就很难解决的问题,而且人工智能还让情况变得更糟。
关于 Zig 项目为何禁止 AI 贡献,人们有很多猜测,但现在你了解了贡献者扑克的重要性,就很容易理解我们为什么要这样做了。
为了能够做出有影响力的工作,贡献者需要熟悉代码库和问题领域,并且需要得到核心团队的信任,相信他们已经仔细考虑了 PR 中引入的所有更改,以力求找到最佳方法,而不是仅仅提交一个碰巧通过 CI 的随机解决方案。
此外,为了提升团队的信任度,贡献者需要在代码合并后的一段时间内继续对其提交的代码负责。人无完人,有时问题会在事后被发现。后续讨论,共同探讨如何纠正错误,正是与那些在特定问题领域积累了深刻见解的工程师建立长期合作关系的重要体现。
这很重要,因为 Zig 的用户反过来也寄希望于 Zig 软件基金会为他们提供一种力求做到最好的语言和工具链。
不幸的是,基于LLM的贡献对我们来说大多是负面的,从毫无价值、充斥着无稽之谈的“路过式”PR(甚至无法编译,更别提通过CI测试)导致的背景噪音增加,到令人匪夷所思的、长达一万行的首次PR。此外,我们还收到大量表面看起来没问题的PR,其中一些甚至明确声称未使用LLM,但后续讨论却立即揭示出作者其实偷偷地参考了LLM,并将LLM中错误百出的回复直接复述给我们。
需要澄清的是,我们并非认为这就是人工智能的全部。我们并不这么认为。这显然是对该工具的滥用,但这也正是我们项目中绝大多数基于LLM的贡献的现状。
因此,虽然理论上有人可能通过利用 LLM 成为有效的贡献者,但从贡献者扑克的角度来看,当我们还有大量其他贡献者不存在这种风险因素时,押注 LLM 用户就显得不理性了。
对我们来说,能够为贡献者提供一个引人入胜的生态系统,让他们能够提高系统思维能力,并与其他有能力、值得信赖且多产的工程师互动,是我们商业模式的关键方面。正如我之前提到的,Zig 的表现远超预期。我们投入了大量精力来思考围绕该项目的技术、社会和商业管理问题。
贡献者扑克是我们战略的关键组成部分,任何阻碍我们有效参与游戏的因素都符合项目的最佳利益。话虽如此,我们也意识到目前仍有许多问题尚未解决,我们将根据实际情况调整政策。
凭什么让 C 开发者集体兴奋
Zachel(2026-06)
一、C 开发者,从来都不缺替代品
C 开发者是软件世界里最特殊的一群人。他们见过太多"下一代系统语言"。从 1980 年代的 Modula-2,到 1990 年代的 C++ 大规模入侵,再到 2000 年代 D 语言的跃跃欲试,2006 年 Rust 的原型出现,2009 年 Go 的横空出世,每隔几年,总有人带着一套新的语言设计走到 C 开发者面前,拍着胸脯说:"这次真的不一样。"
C 开发者的反应从来都很一致:先假装没看见,等你出了生产事故再说。
这不是傲慢,这是经验。他们知道,要真正取代 C,不是"我比 C 更安全"就够了。Rust 做到了内存安全,但 Rust 的学习曲线把大部分系统程序员挡在门外,borrow checker 带来的心智负担让本已复杂的系统代码雪上加霜。Go 做到了工程效率,但 GC 的存在让它天然无缘 kernel、firmware、实时系统。C++ 堆叠了四十年特性,如今没有人敢说自己精通 C++ 全集。
所以当 Zig 出现的时候,大部分人的第一反应依然是怀疑。但这一次,有些事情不太一样。
2019 年前后,一批真正的 C 老兵开始在 Hacker News 上认真讨论 Zig。不是初学者的好奇,不是媒体的炒作,是那些写过 libc、写过内核驱动、维护过十万行 C 代码库的人,在认真说:"这个东西值得看。"
是什么让这群最难取悦的程序员集体动心?
这个问题的答案,不在于 Zig 有多少新特性,而在于 Zig 击中了 C 开发者几十年来最深的痛处——那些痛处,没有任何一门语言真正碰过。
二、C 的问题,从来不是 C 本身
要理解 C 开发者为什么兴奋,首先要理解 C 开发者的真实处境。C 语言本身的设计并不是问题。
struct、指针、手动内存管理、零运行时开销——这些不是缺陷,这是设计目标。C 语言最初在 PDP-11 上诞生,Dennis Ritchie 的目标就是写一个能直接操作硬件、无需运行时支持的系统语言。这个目标,C 完成得极其漂亮。
C 的问题,是它四十年没有更新工具链认知,而软件复杂度早已不可同日而语。
构建系统:一个永远没有答案的问题
用 C 写过任何规模稍大的项目的人都知道,最痛苦的不是写代码,是构建。
Makefile 是 1976 年设计的。它的依赖模型基于文件时间戳,遇上并行构建、跨平台、条件编译就开始崩溃。于是有了 Autotools,写一堆 configure.ac 和 Makefile.am,生成 configure 脚本,生成 Makefile。Autotools 本身成了另一个需要学习的语言,而且是一个极其晦涩的语言。
CMake 出现,解决了跨平台问题,但引入了另一套语法,cmake 脚本写着写着就变成第三个需要维护的代码库。然后是 Ninja、Meson、Bazel、Buck、Premake……每隔几年一个新工具,但没有一个成为真正的标准。
问题的本质不是工具太少,而是 C 生态从来没有将"构建"当做语言本身的一部分来设计。构建系统是语言之外的东西,于是构建永远是每个项目自己解决的问题。
今天一个中等规模的 C 项目,可能同时存在:
Makefile
CMakeLists.txt
configure.ac
meson.build
.github/workflows/build.yml
这不是特例,这是常态。
包管理:C 生态的原罪
Go 有 go get,Rust 有 cargo,Python 有 pip,Node 有 npm。
C 有什么?
#include <openssl/ssl.h> 然后祈祷目标系统装了对版本的 OpenSSL。
C 的包管理问题由来已久,但从未被真正解决,因为解决它需要改变整个生态的工作方式,而 C 生态太庞大、太分散、太保守。conan、vcpkg、pkg-config 各自为战,没有一个成为真正的标准,没有一个真正好用。
这导致一个荒诞的现象:2024 年,一个用 C 编写的开源项目,给新贡献者的第一道门槛往往是"如何把依赖装好"。不是理解代码,不是修 bug,是先花半天时间配置构建环境。
undefined behavior:定时炸弹
这是 C 最为人诟病的设计,但也是最容易被误解的问题。
undefined behavior(UB)不是 C 的设计失误,它是一个有意识的选择:通过不规定某些行为,给编译器优化留下空间,同时允许在不同平台上有不同实现。但 UB 的问题在于,它是隐形的。
int arr[10];
for (int i = 0; i <= 10; i++) {
arr[i] = i; // i=10 时越界,UB
}
这段代码在 debug build 里可能跑得好好的,release build 里编译器可能做出任何事情。不是崩溃,是任何事情。
更危险的是,编译器可以基于"不存在 UB"的假设来做优化。这意味着你写了有 UB 的代码,编译器可能不是让它崩溃,而是"优化"掉了某个安全检查,因为编译器认为那个检查是多余的——基于你的代码没有 UB 的假设。
CVE 数据库里有相当一部分漏洞,根源在于 UB 与编译器优化的组合效应。这不是偶发事故,这是系统性问题。
跨平台:永远的手工活
C 理论上是可移植的,但实践中,跨平台 C 代码是一场噩梦。
#ifdef _WIN32
#include <windows.h>
#define THREAD_CREATE(fn, arg) CreateThread(NULL, 0, fn, arg, 0, NULL)
#elif defined(__linux__)
#include <pthread.h>
#define THREAD_CREATE(fn, arg) pthread_create(...)
#elif defined(__APPLE__)
// Apple's special case
#endif
这种代码在任何中等规模的 C 项目里都随处可见。不是因为程序员懒得抽象,而是底层 API 本身就是平台差异的,强行抽象会有性能代价和语义丢失。于是 C 的跨平台就变成了每个项目自己维护的 #ifdef 矩阵。
这四个问题——构建、包管理、UB、跨平台——是 C 开发者日复一日、年复一年消耗精力的地方。不是写代码的时候,是写代码之外的地方。而这恰恰是历史上每一门想取代 C 的语言都没有正面回答过的问题。
Rust 解决了内存安全,但引入了 cargo 的同时并没有解决与 C 生态互操作的摩擦成本。Go 解决了工程效率,但 GC 画出了边界。C++ 继承了 C 的所有问题,再在上面加了自己的。
Zig 是第一门语言,把这几个问题放在核心位置来设计。
三、失败的前辈们
在 Zig 之前,有几次认真的尝试,值得回顾,因为它们的失败恰好定义了问题的形状。
D 语言:功能最全,生态最死
D 大约在 2001 年由 Walter Bright 设计,目标就是"更好的 C++"。从技术上讲,D 是一门非常有趣的语言:它有 GC 也有手动内存管理,有模板元编程,有 scope(exit) 这种 RAII 的优雅变体,有 immutable,有很多超前的设计。
D 失败的原因不是技术,是路线分裂。它有两个标准库:Phobos 和 Tango,两者不兼容,社区在 2000 年代中期因此分裂。更根本的问题是,D 从来没有解决与 C 的互操作问题——调用 C 库需要手写绑定,没有自动化工具,没有标准流程。
结果是,C 开发者迁移到 D 的成本并不比迁移到 Java 低多少。
Ada:正确但无法使用
Ada 是另一个极端。Ada 在安全性上接近完美:强类型、合同编程、并发模型优秀。今天依然在航空、军事、铁路等领域有使用。
但 Ada 的问题是,它把"形式化正确"当做了第一优先级,把"工程实用"放在了后面。Ada 的工具链昂贵,生态封闭,学习曲线陡峭,与 C 的互操作同样摩擦极大。而且 Ada 从来没有真正进入过 Linux 生态——而 Linux 才是当今系统编程的主战场。
Rust 的真实处境
Rust 是这些前辈里迄今为止最成功的,Mozilla 的背书、内存安全的承诺、cargo 的优秀体验,让它在 2015 年之后快速积累了一批真正的用户。但 Rust 对 C 开发者的吸引力,始终有一道无形的门槛。
这道门槛不是 borrow checker——虽然 borrow checker 确实需要学习。真正的门槛是,Rust 的设计哲学与 C 的设计哲学在根本上是不同方向的。
C 的哲学是:给程序员最大控制权,相信程序员知道自己在做什么。
Rust 的哲学是:通过类型系统和编译时检查,强制程序员证明代码是安全的。
这不是价值判断,而是设计选择。对于一部分 C 开发者来说,Rust 的方向完全正确,他们接受了这个交换。但还有另一部分 C 开发者,他们不需要一个"更安全"的保姆,他们需要的是一个"更好用"的工具。
这部分人,是 Zig 的真正受众。
四、Zig 的核心设计赌注
理解 Zig,必须先理解 Andrew Kelley 在 2015 年前后设计它时,做出了哪些根本性的赌注。不是特性列表,是赌注。每一个赌注都意味着放弃了某些东西。
赌注一:构建系统就是语言本身
这是 Zig 最激进的设计决策,也是最被低估的决策。
Zig 没有 Makefile,没有 CMake,没有任何外部构建系统。Zig 的构建系统是用 Zig 本身写的:
// build.zig
conststd = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "myapp",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
}
这段代码不是 DSL,不是配置文件,这是真实的 Zig 程序,在编译期执行,描述构建过程。
这意味着什么?
意味着你用来写业务逻辑的同一门语言,可以用来描述构建逻辑。没有上下文切换。没有"这个要在 CMake 层处理,那个要在 Makefile 层处理"。调试构建脚本和调试应用代码用同一个工具。
更重要的是,build.zig 是编译期代码,可以有真实的控制流:
pub fn build(b: *std.Build) void {
if (b.host.result.os.tag == .windows) {
// Windows 特定配置
} else if (b.host.result.os.tag == .linux) {
// Linux 特定配置
}
// 条件依赖
if (some_feature_flag) {
exe.linkSystemLibrary("openssl");
}
}
这不是 CMake 那种"脚本化"的构建配置,这是真实的编程。C 开发者几十年来梦寐以求的,把构建逻辑和代码逻辑统一到同一个语言里,Zig 做到了。
赌注二:Zig 是最好的 C 编译器
这句话听起来很奇怪。Zig 不是已经有自己的语言了吗?
但 Andrew Kelley 的设计目标里,Zig 工具链本身就是一个可以编译 C/C++ 的编译器——不是通过调用 clang,而是 Zig 工具链内置了 clang,暴露了 zig cc 和 zig c++ 命令。
# 用 Zig 编译 C 代码
zig cc -o hello hello.c
# 交叉编译到 ARM
zig cc -target aarch64-linux-musl -o hello_arm hello.c
# 交叉编译到 Windows
zig cc -target x86_64-windows-gnu -o hello.exe hello.c
这意味着什么?
意味着一个 C 项目,可以在不改任何代码的情况下,用 Zig 工具链做到:
零配置交叉编译
静态链接 musl libc
生成各平台原生可执行文件
zig cc 已经被 Uber、TigerBeetle、Bun 等公司用于交叉编译 C/C++ 项目。这不是实验,这是生产环境。
为什么这对 C 开发者是革命性的?
因为跨平台交叉编译在传统 C 工具链里是一件极其痛苦的事。你需要安装对应平台的交叉编译工具链,配置 sysroot,处理 libc 版本,祈祷动态链接不出问题。zig cc 把这个过程变成了一个参数。
赌注三:comptime 而非宏
C 开发者对宏的感情是复杂的。宏是 C 里唯一的元编程工具,但它是文本替换层面的,没有类型,没有作用域,调试噩梦。
// C 宏的经典陷阱
#define SQUARE(x) x * x
int result = SQUARE(1 + 2); // 展开为 1 + 2 * 1 + 2 = 5,不是 9
C++ 有模板,Rust 有宏系统,但都是在类型系统上叠加的复杂机制。
Zig 的 comptime 是一个完全不同的思路:
fn add(comptime T: type, a: T, b: T) T {
return a + b;
}
const result = add(i32, 1, 2); // 编译期就知道 T = i32
comptime 的核心思想是:编译期和运行期共用同一套语言。你写的 Zig 函数,可以在编译期执行,也可以在运行期执行,取决于参数是不是 comptime 已知的。
fn printType(comptime T: type) void {
if (T == i32) {
std.debug.print("int32\n", .{});
} else if (T == f64) {
std.debug.print("float64\n", .{});
}
}
这段代码里的 if 在编译期就被求值了,不存在运行时分支。相比 C 的 #ifdef 宏,comptime 是类型安全的、可调试的、可测试的。
对 C 开发者来说,comptime 是那种"我一直需要但不知道怎么要"的东西。
赌注四:显式优于隐式
这是 Zig 设计中最具争议,也最值得深思的决策。
Zig 没有隐式类型转换:
var x: i32 = 5;
var y: i64 = x; // 编译错误!必须显式转换
var y: i64 = @intCast(x); // 正确
Zig 没有隐式内存分配。任何可能分配内存的函数,必须接受一个 allocator 参数:
// 标准库的 ArrayList
var list = std.ArrayList(i32).init(allocator);
defer list.deinit();
try list.append(1);
没有哪个标准库函数会在你不知情的情况下调用 malloc。
这看起来很烦。实际上这是 C 开发者梦寐以求的透明度。
C 代码里最常见的 bug 之一,是"哪个库在什么时候分配了内存"。strdup 分配,sprintf 可能分配,某个深藏库函数分配,然后崩溃,然后 valgrind,然后花三小时找到调用链。
Zig 把这个问题彻底消灭了:没有 allocator 参数的函数,不分配内存,句号。
五、实现机制:Zig 工具链的内部构造
理解 Zig 对 C 开发者的吸引力,必须看一眼 Zig 工具链的架构。
zig 工具链
┌───────────────────────────────────────┐
│ │
│ zig build-exe / zig build-lib │
│ │ │
│ ▼ │
│ Zig 前端 (AST → IR) │
│ │ │
│ ▼ │
│ Zig IR (中间表示) │
│ │ │
│ ┌──────┴──────┐ │
│ │ │ │
│ ▼ ▼ │
│ LLVM C 后端 │
│ │ │ │
│ ▼ ▼ │
│ 目标代码 可移植 C 代码 │
│ │
│ zig cc / zig c++ │
│ │ │
│ ▼ │
│ Clang (内置) │
│ │ │
│ ▼ │
│ LLVM │
└───────────────────────────────────────┘
几个关键设计点:
自包含工具链。Zig 的发行版是一个单一的二进制文件,包含了编译器、链接器(基于 LLD)、libc 头文件(多平台),以及 Clang。下载一个文件,就有了完整的 C/C++/Zig 开发环境。这对于 CI/CD 系统是革命性的——不需要在构建容器里安装工具链,不需要管理工具链版本依赖。
musl libc 内置。Zig 工具链内置了编译好的 musl libc,这意味着可以一键静态链接,生成无依赖的可执行文件:
zig build-exe main.zig -target x86_64-linux-musl
这个命令生成的二进制文件,在任何 x86_64 Linux 上都能运行,不依赖任何动态库。
C 后端。除了 LLVM 后端,Zig 还有一个 C 后端,可以把 Zig 代码编译成 C 代码。这让 Zig 可以在任何只有 C 编译器的平台上运行。自举编译器的设计里这一点尤其重要。
Allocator 的内存模型
Zig 的 Allocator 系统值得单独分析,因为它体现了 Zig 对"显式优于隐式"的彻底贯彻。
Zig 的 std.mem.Allocator 是一个接口(在 Zig 里用 vtable 实现):
pub const Allocator = struct {
ptr: *anyopaque,
vtable: *const VTable,
pub const VTable = struct {
alloc: *const fn (ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8,
resize: *const fn (ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool,
free: *const fn (ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void,
};
};
这个接口的设计有几个微妙之处:
resize 操作是 in-place 的尝试,返回 bool 表示是否成功。这让 allocator 可以实现比 realloc 更精细的控制——某些 allocator(比如 ArenaAllocator)可以非常廉价地实现 resize。
free 接受的是 []u8 而不是指针,也就是说 free 知道释放的内存大小。这是与 C 的 free(void*) 的根本区别——C 的 free 必须在分配器内部维护大小信息,而 Zig 的 allocator 可以把这个信息推给调用方,允许实现更简单、更快速的 allocator。
标准库提供了几种 allocator:
GeneralPurposeAllocator ← 通用,debug 模式下检测泄漏
ArenaAllocator ← 批量分配,一次性全部释放
FixedBufferAllocator ← 在栈上或预分配 buffer 上分配,零 syscall
PageAllocator ← 直接 mmap,适合大块分配
在嵌入式或 WASM 场景下,你可以提供一个 FixedBufferAllocator,整个程序不会有任何动态内存分配:
var buffer: [1024 * 1024]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
// 所有内存操作都在这个 buffer 上进行
var list = std.ArrayList(i32).init(allocator);
对嵌入式 C 开发者来说,这种模式并不陌生——静态内存池在嵌入式里很常见。但 Zig 把它变成了一等公民,标准库完全适配,不需要任何改造。
Error Model
Zig 的 error handling 也是 C 开发者关注的点,因为它解决了 C 里一个经典的痛点。
C 的错误处理是约定俗成的:返回 -1 或 NULL,调用者检查,通过 errno 获取错误类型。这套机制有几个问题:忘记检查返回值是合法 C,编译器不会报错;errno 是全局变量,在多线程里需要特殊处理;错误类型的表达能力有限。
Zig 的 error 是编译器一等支持的:
const FileError = error{
NotFound,
PermissionDenied,
OutOfMemory,
};
fn readFile(path: []const u8) FileError![]u8 {
// ...
}
// 调用
const data = try readFile("config.txt");
// try 会在出错时直接 return error
try 关键字相当于:
const data = readFile("config.txt") catch |err| return err;
关键点是:! 在类型里意味着这个函数可能失败,编译器强制要求调用方处理 error,不处理就无法编译。这在语义上比 C 的"约定返回 -1"要严格得多,但在语法上比 Rust 的 Result<T, E> 要轻量。
六、zig cc 的工程价值:案例分析
这一节必须讲真实案例,因为这是 Zig 吸引 C 开发者最有说服力的证据。
Bun 的构建
Bun 是一个 JavaScript 运行时,核心用 C++ 和 Zig 混合编写。Bun 的构建系统完全基于 zig cc。它需要在 macOS (x86_64, arm64)、Linux (x86_64, arm64)、Windows 上分发二进制。传统方案是维护多个 CI 节点,分别在各个平台构建。Bun 用 zig cc 在单台 Linux 机器上做交叉编译,生成所有平台的二进制。
这不只是省了 CI 成本,更重要的是确保了构建环境的一致性。所有平台的二进制由同一台机器、同一个编译器版本生成,没有"在我机器上好好的"问题。
TigerBeetle 的零依赖哲学
TigerBeetle 是一个用 Zig 写的金融数据库,以极端的性能和可靠性著称。其的构建策略是:发行版二进制必须是单一文件,无任何运行时依赖,包括 libc。
zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe
这个构建输出一个静态链接了 musl libc 的二进制文件。在任何 x86_64 Linux 机器上,./tigerbeetle 就能运行,不需要安装任何东西。
对于金融基础设施来说,这种零依赖是极其重要的:部署简单、安全面小、没有动态链接漏洞面。
Zig 被用于编译 FFmpeg
这是一个更典型的"zig cc 作为 C 编译器"场景。
FFmpeg 是一个数百万行的 C 代码库,从来没有考虑过 Zig。但有人用 zig cc 替换了 FFmpeg 的构建里的 gcc,然后用 -target 做交叉编译。没有改任何 FFmpeg 代码,直接工作。
这验证了 zig cc 作为 drop-in C 编译器替代品的可行性。
七、性能分析
性能对 C 开发者来说不是可选项,是基本条件。
编译产物的运行时性能
Zig 通过 LLVM 后端生成代码,与 Clang 共享同一套优化管线。所以 Zig 代码的运行时性能与 Clang 编译的 C 代码基本相当。
但有几个地方 Zig 可以做得更好:
更精确的 undefined behavior 语义。Zig 的 undefined behavior 比 C 少很多,而且剩余的 UB 在 Debug 和 ReleaseSafe 模式下有明确的运行时检查。这意味着 Zig 代码在安全模式下可以捕获 C 里那些"靠编译器优化消除掉但实际上有 bug"的场景。
强制的 align 信息。Zig 类型系统要求显式指定对齐,这让编译器可以做更激进的 SIMD 向量化:
// 明确 32 字节对齐,允许 AVX2 向量化
const Vec = struct {
data: [8]f32 align(32),
};
comptime 零成本抽象。泛型代码在编译期特化,没有运行时虚表开销:
C++ 虚函数调用:
load vtable ptr
load function ptr from vtable
indirect call
Zig comptime 泛型:
直接 call(编译期已特化)
构建时间
这是 Zig 目前的弱点,需要客观承认。
Zig 编译器本身的编译速度,比 Clang 慢。这是因为 Zig 做了很多编译期工作(comptime 执行),以及语义分析的复杂度更高。
对于小型项目,这差别不明显。对于百万行级别的代码库,增量编译的速度会是一个真实的工程问题。
Zig 团队知道这个问题,增量编译系统是他们的主要开发方向之一,但截至目前还没有达到 Clang 的增量编译效率。
内存使用
Zig 的 ArenaAllocator 在特定场景下可以显著优于 glibc malloc:
glibc malloc:
每次 alloc: ~100ns (无竞争)
每个对象: 16 字节 metadata 开销
碎片化: 随时间增加
ArenaAllocator:
每次 alloc: ~2ns (bump pointer)
每个对象: 0 字节 metadata 开销
碎片化: 不存在(批量释放)
对于解析器、编译器、请求处理器这类"per-request arena"场景,ArenaAllocator 不只是理论上更好,而是有量级差距。
八、与其他语言的客观对比
| 维度 | C | Zig | Rust | C++ | Go |
| 运行时性能 | ★★★★★ | ★★★★★ | ★★★★★ | ★★★★★ | ★★★★ |
| 内存安全 | ★★ | ★★★ | ★★★★★ | ★★ | ★★★★ |
| 编译速度 | ★★★★★ | ★★★ | ★★★ | ★★ | ★★★★★ |
| 学习成本 | ★★★★ | ★★★★ | ★★ | ★ | ★★★★★ |
| 跨平台构建 | ★★ | ★★★★★ | ★★★★ | ★★ | ★★★★ |
| 与 C 互操作 | — | ★★★★★ | ★★★ | ★★★★ | ★★ |
| 工具链统一 | ★ | ★★★★★ | ★★★★ | ★★ | ★★★★★ |
| 生态成熟度 | ★★★★★ | ★★ | ★★★★ | ★★★★★ | ★★★★★ |
几点说明:
Rust 在内存安全上无可争议地领先。Zig 的安全模型(运行时检查 + 显式分配)比 C 强,但与 Rust 的编译期证明没有可比性。对于需要证明内存安全的场景(内核驱动、安全关键软件),Rust 是更好的选择。
Go 在工程效率上无可争议地领先(对于非系统编程场景)。Zig 的学习曲线对于 Go 开发者来说反而更高,因为 Go 的设计目标完全不同。
Zig 与 C 的互操作是其他语言无法比拟的。这不只是"可以调用 C 函数",而是 Zig 可以直接 @import C 头文件,类型系统自动映射,不需要手写绑定。
生态成熟度是 Zig 最大的弱点,这必须正视。Zig 目前还没有 1.0,标准库 API 还在变化,第三方库生态与 C/C++/Rust 相比极度贫乏。用 Zig 写生产代码,意味着很多轮子需要自己造,或者直接调用 C 库。
九、工程价值:什么时候用 Zig,什么时候不用
适合的场景
CLI 工具:Zig 非常适合写系统管理工具。单一二进制、静态链接、启动快、内存可控。zig cc 的交叉编译能力让 CLI 工具分发变得极其简单。
嵌入式和 WASM:Zig 的 freestanding 目标(无操作系统)是一等支持的。没有 GC,没有隐式分配,编译器知道整个调用栈的内存行为。WASM 目标同理——Zig 的 WASM 支持是其他语言里最干净的之一。
数据库和游戏引擎:需要精细内存控制的场景。TigerBeetle 的存在证明了这一点。游戏引擎里的内存分配策略(帧分配器、池分配器)与 Zig 的 allocator 模型完美匹配。
现有 C 项目的渐进改造:这是被严重低估的场景。Zig 可以一次替换一个文件,与现有 C 代码无缝链接。团队不需要"全面迁移",可以在最需要改进的地方先用 Zig,其余保持 C。
构建工具链:zig cc 作为跨平台 C 编译器的使用价值,完全独立于 Zig 语言本身。即便你不写一行 Zig 代码,zig cc 也可能是你工具箱里最值钱的工具。
不适合的场景
需要成熟生态的项目:如果你需要大量第三方库,Zig 目前的生态无法满足。这不是技术问题,是时间问题,但当下是现实。
需要强安全性保证的项目:内核安全模块、密码学库、需要形式化验证的代码——Rust 的内存安全保证在这些场景里比 Zig 的运行时检查更可靠。
团队不了解系统编程的项目:Zig 的设计假设你知道内存是怎么工作的。如果团队对堆/栈分配、对齐、指针运算没有基础认知,Zig 会比 Go 或 Python 更危险,不是因为它不安全,而是因为它把这些决策显式暴露给了程序员。
需要快速迭代的应用层代码:Zig 在应用层写 JSON 处理、HTTP 服务器、业务逻辑,并没有比 Go 或 Rust 更好的体验,反而因为生态不成熟而更麻烦。
十、行业影响
Zig 的成功不会以"取代 C"来定义,因为 C 不会被取代。C 已经是基础设施,就像汇编语言没有被 C 取代一样,C 也不会被任何单一语言取代。
Zig 真正可能改变的是两件事:
重新定义"系统语言工具链"的标准。如果 zig cc 的跨平台编译范式被接受,那么未来的系统语言工具链必须回答:为什么我的工具链不能像 Zig 一样自包含?为什么交叉编译不是零配置的?这个标准一旦被接受,整个行业的工具链设计都会受到压力。
重新思考"安全"的成本。Zig 提出了一个不同于 Rust 的答案:安全不一定要通过类型系统在编译期证明,也可以通过显式控制和运行时检查来实现。这两种哲学没有对错之分,但它们代表了不同的工程权衡。Zig 让这个权衡变得可见,迫使整个行业更清晰地思考"我到底需要什么程度的安全保证"。
Zig 当前最大的挑战是可信度窗口。语言还没有 1.0,API 还在变化,用于生产的公司需要承担"跟着版本更新重构"的成本。这个挑战不是技术问题,是信任问题。等 Zig 稳定到 1.0,那批早期用 Zig 写的系统是否依然稳固,将会是决定性的证据。
如果失败,原因大概率不是技术,而是那个永恒的问题:生态。一门语言的生命力,最终取决于有多少人写了多少有用的库。Zig 现在的处境与 2012 年的 Rust 类似——技术上很有趣,但生产环境里到处是空架子。Rust 用了十年时间建立起生态,Zig 才刚刚开始。
十一、未来思考
有一件事值得停在这里想一想。
C 开发者集体对 Zig 产生兴趣,不只是因为 Zig 的技术设计好,更是因为这群人等得太久了。
他们等一门语言,不用教他们什么是指针,不用强迫他们接受 GC,不用让他们学一套新的类型理论才能写一个简单的 server。他们等一门语言,能真正理解"我有一个 C 代码库,我不打算扔掉它,但我想在新的部分用更好的工具"。
Zig 是第一门语言,设计者本人像是在跟 C 开发者说:我知道你在痛什么,我也痛过。
这种共情,和那些从学术出发设计语言的项目是根本不同的。
Andrew Kelley 在写 Zig 之前,写的是 C 和 C++ 游戏引擎代码。他痛过 Makefile,痛过宏,痛过 malloc 追踪不到的泄漏,痛过 undefined behavior 的幽灵行为。Zig 不是一个"理论上更好"的语言,它是一个工程师的愤怒和耐心的结晶。
但这里有一个更深的问题还没有被回答:
C 语言之所以存在了五十年还无法被替代,并不只是因为它的代码库太大,或者生态太成熟。更深的原因是,C 的设计哲学与硬件之间有一种接近直觉的对应关系——你写下的代码,和机器实际做的事情,之间几乎没有抽象层。
Zig 能否在提供更好工具的同时,保持这种透明度?
comptime 是对的,allocator 接口是对的,显式错误处理是对的。但当代码库规模到了数百万行,当团队规模到了数百人,语言的设计决策会以你意想不到的方式显现出来。
也许那个时候,Zig 最有价值的地方,不是它在 1.0 时做到了什么,而是它在设计过程中,迫使整个行业重新问了一遍那些本该一直被问的问题:
一门系统语言,到底在为谁服务?
是为编译器理论的优雅服务,还是为每天在终端里 make && ./a.out 的工程师服务?
这个问题的答案,比任何一门语言的命运都要更持久。