为什么Linux内核选择了Rust而不是Zig?

2022年12月,Linux 6.1正式发布,首次将Rust作为内核的第二种编程语言。本文深入分析了Linux内核选择Rust而非Zig的核心原因,包括时机、语言特性差异、社区生态等多个维度,并探讨了两种语言在系统编程领域的不同定位。

引言

在众多现代系统编程语言中,为什么是Rust获得了Linux内核”第二语言”的席位,而同样优秀的Zig却未能入选?这个问题的答案,远比表面看起来要复杂得多。

最直接的原因可以概括为:当内核在2022年底正式引入Rust时,Zig还没准备好;而当Zig逐渐成熟时,内核的”第二语言”席位已经被Rust占据。2022年10月,Linus Torvalds将Rust代码合并到Linux 6.1的开发周期中1,同年12月11日,Linux 6.1正式发布2,Rust成为Linux历史上首次被接纳的C语言之外的编程语言3

这背后是工程决策、语言特性和社区生态共同作用的结果。

核心原因分析

⏳ 时机与行业背书

Rust for Linux项目于2020年在Linux内核邮件列表中宣布启动4,经过两年多的开发,于2022年12月随Linux 6.1正式进入稳定版本。在2019-2020年内核讨论引入第二语言时,Zig(2015年诞生)还处于早期阶段,而Rust背后有Mozilla、微软、谷歌等巨头的投入。到2025年12月,Rust已正式从”实验性”状态转为Linux内核的核心组成部分5

🔧 语言特性的根本差异

Rust和Zig的设计哲学存在本质差异,这决定了它们与内核需求的匹配度。

Rust:激进的安全卫士

核心目标是在编译期消除内存错误。它通过所有权、生命周期等机制,在编译阶段就堵死空指针、数据竞争等漏洞,这直击了内核安全最核心的痛点。研究表明,约70%的内核安全问题源于内存安全,而Rust可以自动消除其中的大部分6。宏和RAII特性也被驱动开发者视为处理复杂硬件逻辑的利器。

Zig:现代的C语言替代者

旨在成为C的现代升级版,强调对底层操作的完全掌控“零隐式行为”7。它没有Rust那样复杂的编译器,依靠显式的错误处理编译期执行来提升C的安全性。但对于内核开发者,这意味着需要手动管理资源,并可能面临段错误。

🌍 社区与生态的门槛

对Linux这样的超大规模项目,生态是关键。Rust拥有庞大的用户群和库,这为内核的长期维护和人才储备提供了保障8。相比之下,Zig在2015年才诞生,其生态系统和开发者社区规模相对较小,语言本身也仍在快速演进中,这在一定程度上增加了项目采纳的风险。

🚫 为什么不是C++?

在讨论为何选择Rust而非Zig之前,一个更基本的问题是:为什么Linux内核从未考虑使用C++?毕竟C++也提供了RAII等现代特性。

Linus Torvalds对此有过非常明确的表态。早在2004年,他就在Linux内核邮件列表中指出9

“写内核代码用C++是一个非常愚蠢的想法(BLOODY STUPID IDEA)。”

“It sucks. Trust me - writing kernel code in C++ is a BLOODY STUPID IDEA.”

“事实上,C++编译器是不可信的…C++的整个异常处理机制从根本上就是有问题的,对内核来说尤其如此。”

“The whole C++ exception handling thing is fundamentally broken. It’s _especially_ broken for kernels.”

2007年,在Git邮件列表上,Linus更系统地阐述了反对C++的理由10

1. 异常处理机制不适合内核

C++的异常处理会引入非局部控制流跳转,这在需要绝对确定性的内核代码中是不可接受的。C语言的错误返回值机制虽然繁琐,但路径清晰、透明可控。Linus早在2004年就明确指出9

“The whole C++ exception handling thing is fundamentally broken. It’s _especially_ broken for kernels.”

“C++的整个异常处理机制从根本上就是有问题的,对内核来说尤其如此。”

学术研究也印证了这一问题。2019年爱丁堡大学的研究表明,即使采用优化后的实现,C++异常处理在嵌入式系统中仍然存在显著的代码体积和运行时开销11。2025年St Andrews大学的最新研究指出,C++异常在用户态/内核态边界的传播需要特殊的ABI支持,增加了系统复杂性12

2. 隐式内存分配是大忌

内核需要对每一个字节的内存分配有完全的控制权。Linus在2004年明确指出9

“Any compiler or language that likes to hide things like memory allocations behind your back just isn’t a good choice for a kernel.”

“任何喜欢在背后隐藏内存分配等操作的编译器或语言,都不是内核开发的好选择。”

3. 抽象导致的效率问题

Linus在2007年指出10

“C++ leads to really really bad design choices. You invariably start using the ‘nice’ library features of the language like STL and Boost and other total and utter crap, that may ‘help’ you program, but causes… inefficient abstracted programming models where two years down the road you notice that some abstraction wasn’t very efficient, but now all your code depends on all the nice object models around it, and you cannot fix it without rewriting your app.”

“C++导致真正糟糕的设计选择。你不可避免地会开始使用STL和Boost等’优雅的’库特性…这会导致低效的抽象编程模型,两年后你会发现某些抽象效率不高,但现在你所有的代码都依赖于这些精美的对象模型,除非重写应用,否则无法修复。”

4. C语言足以实现面向对象

Linus在2004年指出9

“You can write object-oriented code (useful for filesystems etc) in C, _without_ the crap that is C++.”

“你可以用C编写面向对象的代码(对文件系统等很有用),而不需要C++中的那些垃圾。”

Linux内核用C语言的结构体和函数指针实现了充分的面向对象设计。

这些观点揭示了Linux内核对于编程语言的核心要求:透明性、可控性和确定性。C++虽然功能强大,但其隐式行为和复杂的抽象机制与内核开发的哲学背道而驰。

相比之下,Rust通过所有权系统在编译期强制执行安全规则,没有运行时开销,且所有的内存管理都是显式的。Zig则更进一步,完全消除了隐式行为。这两种语言都比C++更符合内核开发的需求。

💡 Zig的现状与角色

尽管没能成为内核的”第二语言”,Zig在Linux生态中正找到一个独特的切入点。Zig凭借其出色的交叉编译能力精细的内存控制,正成为优化系统工具和基础设施的有力选择13。其内置的构建系统和工具链,即使在传统的C/C++项目中也展现出显著的优势。

深入理解:什么是RAII?

在讨论Rust的优势时,RAII是一个绕不开的话题。

RAII是Resource Acquisition Is Initialization(资源获取即初始化)的缩写。它在C++中普及,并被Rust等语言继承和发展,是管理内存、文件句柄、锁等系统资源的核心范式。

核心思想是:将资源的生命周期,与对象的生命周期严格绑定

工作原理

简单来说,RAII通过构造函数和析构函数这对”钩子”,实现了资源的自动管理:

这确保了资源绝不会泄漏,即使发生异常,只要对象被销毁,析构函数就一定会被调用,实现异常安全

Rust中的自动释放机制

Rust通过Drop trait实现析构函数14。当变量离开作用域时,Rust编译器会自动调用该类型的drop方法。以自旋锁为例:

// 简化的SpinLockGuard实现
impl<'a, T> Drop for SpinLockGuard<'a, T> {
    fn drop(&mut self) {
        // 当guard被销毁时,这个方法会自动调用
        self.lock.unlock(); // 释放锁
    }
}

关键机制包括:

  1. 作用域规则:变量在离开其作用域(通常由花括号{}界定)时被销毁15
  2. 自动调用:编译器在编译时就确定在哪里插入drop()调用,这是零成本抽象
  3. 异常安全:即使发生panic或提前返回,drop也会被调用,确保资源释放
{
    let mut guard = spinlock.lock(); // 获取锁

    if error_condition {
        return; // 提前返回
        // guard在此离开作用域,drop被自动调用,锁被释放
    }

    do_something(&mut guard)?; // 如果出错
    // guard在此离开作用域,drop被自动调用,锁被释放

} // 正常情况下,guard在此离开作用域,锁被释放

这就是为什么说”开发者无法忘记解锁” - 不是靠记忆力或代码审查,而是编译器强制保证的。

在内核开发中的价值

对于Linux内核这样的底层系统,RAII的价值巨大。传统C语言使用goto语句集中处理错误,容易遗漏。而RAII可以彻底解决这个痛点。

以Rust代码为例,它展示了如何安全地管理一个内核自旋锁:

// 解锁动作被自动"绑定"到了guard对象上
let mut guard = spinlock.lock(); // `lock()`获取锁,返回一个guard对象
do_something(&mut guard);         // 通过guard访问数据
// guard在此处被销毁,锁被自动释放

开发者无法忘记解锁,即使在do_something中发生错误,锁也会被正确释放。这对于构建高可靠的驱动和内核模块至关重要。

Rust的RAII与所有权

相比C++,Rust将RAII提升到了语言核心位置。通过所有权(Ownership)机制,Rust强制要求每个资源有唯一的所有者。当所有者离开作用域,资源被自动释放,从根本上杜绝了悬空指针和重复释放的问题。

Zig的资源管理方式

Zig采取了不同的设计哲学。虽然Zig提供了defer关键字来简化资源清理(类似Go),但它强调”零隐式行为”7,资源释放需要开发者显式编写,由编译器在编译期验证控制流:

const file = try std.fs.cwd().openFile("file.txt", .{});
defer file.close(); // 必须显式写defer

这种方式给了开发者最大的控制权和可预测性,但在规模庞大、错误路径复杂的Linux内核中,需要人工确保每个分支都正确处理资源释放,审查负担相对较大。

相比之下,Rust的RAII通过类型系统和编译器强制保证资源释放,提供了”自动、安全、无法遗忘”的资源管理能力,更符合内核对安全性的极致要求。

深入理解:Zig相比C的实质性提升

有人可能会认为”Zig相比C提升不大”,这个说法并不准确。如果Zig相比C提升不大,它不会在系统编程社区获得越来越多的关注。

更准确的表述是:Zig在”显式控制”路径上做到了极致,而Rust在”安全抽象”路径上做到了极致。两者都远超C,只是方向不同。

1. 编译期执行(Comptime)

这是Zig最革命性的特性,C完全没有:

// 泛型数据结构 - C需要void*或宏,极其别扭
fn List(comptime T: type) type {
    return struct {
        items: []T,
        len: usize,
    };
}

// 使用
var int_list = List(i32){};
var string_list = List([]u8){};

在C语言中,这要么用宏写出难以调试的代码,要么用void*牺牲类型安全。

2. 真正的错误处理

C的错误处理靠返回值+errno,极易被忽略:

// C - 容易忘记检查返回值
FILE *f = fopen("file.txt", "r");
fread(buf, 1, size, f);  // 如果fopen失败?崩溃!
// Zig - 错误必须处理
const file = try std.fs.cwd().openFile("file.txt", .{});
// 如果openFile失败,try会向上传播错误,不会默默继续
defer file.close();

Zig通过语言机制强制处理错误,但又不像Java的异常那样有运行时开销。

3. 真正的无未定义行为

C语言充满了未定义行为:有符号整数溢出、空指针解引用、缓冲区溢出等。编译器会基于”未定义行为不会发生”做激进优化,导致隐蔽的bug。

Zig定义了所有操作的语义:

4. 交叉编译是一等公民

C的交叉编译是噩梦:需要配置工具链、头文件路径、库路径等。

# Zig - 直接指定目标
zig build-exe --target riscv64-linux-gnu myapp.zig
# 无需安装任何东西,Zig内置了目标平台的libc

5. 构建系统内置,告别make

// build.zig - 这是Zig代码,不是DSL
const exe = b.addExecutable("myapp", "src/main.zig");
exe.linkLibC();
exe.addIncludePath("/usr/include");

C语言从诞生至今都没有语言层面的标准构建系统,依然依赖于makecmakeautotools等第三方工具。

为什么说”提升不大”的错觉存在?

这种印象主要来自内存安全这个最受关注的维度:

方面 C Zig Rust
内存安全 ❌ 全靠人工 ⚠️ 更好的工具(可选检查、显式控制) ✅ 编译器强制保证
错误处理 ❌ 易忽略 ✅ 语言级强制 ✅ 语言级强制
泛型编程 ⚠️ 宏/void* ✅ comptime ✅ 泛型+trait
元编程 ⚠️ 宏预处理器 ✅ comptime ✅ 宏
学习曲线 中等
对现有C代码 - ✅ 良好兼容 ⚠️ 需要FFI绑定

关键区别:Rust说”我替你管,你别操心”,Zig说”我给你最好的工具,你来管”。

Linux内核场景的结论

回到最初的问题:Linux内核为什么没选Zig?

不是Zig不够好,而是内核的需求更匹配Rust的安全哲学

  1. 内核的代价不同:用户态程序的内存漏洞可能导致进程崩溃;内核的内存漏洞则可能导致权限提升、系统崩溃等严重安全问题
  2. C代码的常见缺陷:内核维护者指出,大量bug源于”C语言中那些愚蠢的小陷阱”,包括内存覆写、错误路径清理遗漏、忘记检查错误值和use-after-free错误16,而这些在Rust中完全不存在
  3. 审查负担:Rust让编译器承担了大部分内存安全审查工作17;而Zig虽然提供了更好的工具,但仍需要人工审查每一处潜在的内存安全问题

Zig相比C的提升很大,只是在”内存安全”这个特定维度上,它选择了和C类似的路径——给开发者强大的工具,但不强制安全。这让它成为:

但在Linux内核这种对绝对安全有极致要求的场景,Rust的强制保证确实更胜一筹。

总结

Linux内核选择Rust而非Zig,是在那个时间点上,对安全性、成熟度和生态的综合考量。Rust的编译期内存安全保证、成熟的工具链和庞大的社区,使其成为内核”第二语言”的最佳选择。

而Zig虽然没有进入内核核心,但也凭借其在资源效率和C互操作性上的优势,在Linux生态的外围找到了用武之地。两种语言都在推动系统编程的发展,只是选择了不同的路径。

延伸阅读

参考资料

  1. The Initial Rust Infrastructure Has Been Merged Into Linux 6.1 - Phoronix, 2022年10月报道Rust合并到Linux 6.1 

  2. Linus Torvalds reveals Linux kernel 6.1 - The Register, 2022年12月11日报道Linux 6.1发布 

  3. Linux 6.1 Officially Adds Support for Rust in the Kernel - InfoQ关于Linux 6.1正式添加Rust支持的详细报道 

  4. Rust for Linux - Rust for Linux项目官方网站 

  5. Linux Kernel Adopts Rust as Permanent Core Language in 2025 - WebProNews, 2025年12月报道 

  6. Rust for Linux: Understanding the Security Impact of Rust in the Linux Kernel - 研究论文,分析了Rust在Linux内核中的安全影响 

  7. Why Zig When There is Already C++, D, and Rust? - Zig官方文档对比分析  2

  8. Rust Integration in Linux Kernel Faces Challenges but Shows Progress - The New Stack关于Rust在Linux内核中的进展报道 

  9. Re: Compiling C++ kernel module + Makefile - Linus Torvalds, 2004年1月19日在Linux内核邮件列表的回复  2 3 4

  10. Re: [RFC] Convert builtin-mailinfo.c to use The Better String Library - Linus Torvalds, 2007年9月6日在Git邮件列表关于C++的完整论述  2

  11. Low-cost deterministic C++ exceptions for embedded systems - University of Edinburgh, 2019年ACM编译器构造国际会议论文 

  12. Propagating C++ exceptions across the user/kernel boundary - Voronetskiy & Spink, University of St Andrews, PLOS 2025 

  13. Comparing Rust vs. Zig: Performance, safety, and more - LogRocket技术博客深度对比 

  14. Running Code on Cleanup with the Drop Trait - Rust官方文档,详细介绍Drop trait的工作原理 

  15. RAII - Rust By Example - Rust官方示例,解释RAII模式 

  16. Linux Driver Development with Rust - Apriorit关于Rust驱动开发的分析,引用内核维护者的观点 

  17. How Rust’s Debut in the Linux Kernel is Shoring Up System Stability - Linux Journal关于Rust如何提升内核稳定性 

My Github Page: https://github.com/liweinan

Powered by Jekyll and Theme by solid

If you have any question want to ask or find bugs regarding with my blog posts, please report it here:
https://github.com/liweinan/liweinan.github.io/issues