先说清楚:我并不反对 Rust 本身。
事实上,我一直在用 Rust 开发自己的项目。我喜欢它的所有权系统,理解生命周期和借用检查,也享受它带来的内存安全保证。
但正是因为每天都在用,我才更清楚地看到它的不足——那些让它无法成为一门真正成熟的编程语言的问题。
这些问题对我个人开发没什么影响,但从一门语言的长远发展来看,它们很重要。
这是最基础,也是最致命的问题。
Rust 已经到了 1.0+ 版本,却没有正式的语言规范。
对比一下 ISO C 标准:每一项特性都有三到四个描述片段——正式的语法约束、语义定义、注意事项、示例代码。清晰、完整、可验证。
再看看 Rust 参考文档对"结构体"的描述:
这就完了?
至少缺少这些内容:
一门成熟的编程语言(版本到了 1.0)应该有正式的规范,对编译器开发者和语言使用者都有用。
没有正式规范的直接后果:我经常遇到不确定的行为,不知道是我理解错了,还是编译器理解错了。
let mut iter = "abc".chars();
foo(iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap());
问题来了:是调用 foo('a','b','c') 还是 foo('c','b','a')?
在 C 语言中,这是 undefined,因为取决于参数传递方式。
在 Rust 中,这也是 undefined——因为没有正式规范告诉你它应该是怎样的。
struct Foo { a: i32 }
fn bar(foo: &mut Foo, x: i32) { foo.a = x; }
let mut foo = Foo { a: 0 };
bar(&mut foo, foo.a);
这段代码因为借用问题无法编译。但问题是:编译器不是应该"聪明地"在调用前创建 foo.a 的副本吗?
有人告诉我新版本编译器处理得很好,但问题仍然存在——这是编译器的问题,还是调用定义发生了变化?
没有规范,我不知道。
理解所有权、生命周期、借用,这些都没问题。
但 trait 几乎每次都会让我抓狂。
我知道 trait 被实现成"调用表"(vtable),但问题是:
trait Foo: Bar)转换成子 trait(&Foo -> &Bar)需要大量样板代码?Box 转换后无法找回原对象?一门成熟的编程语言不应该只有一个编译器。这是 Rust 的另一个问题。
自举过程非常糟糕。
看看 Guix 对 C 编译器的自举:
Rust 呢?
不能用 rustc 1.36 直接编译 1.46。这意味着什么?如果中间某个版本有问题,整个链条就断了。
理论上,应该有一种"方言"编译器,用老版本能理解的方式开发新版本,实现更优雅的升级路径。
LLVM 确实有很多优点:跨平台代码生成、优化、不用自己实现后端。
但它也有代价:
面向系统编程的语言,除了高级代码外还应该支持汇编。
Rust 对汇编的支持很差。虽然可以用 build.rs 调用外部汇编器,但"一点也不好"。
一门真正的系统语言,应该原生支持汇编文件,即使不像 GAS 那样提供丰富的预处理器语法。
Rust 标准库对操作系统交互的支持很有限。
如果我想对任意 UNIX 系统做一些事情,至少需要:
libc crate一种是把 musl 翻译成 Rust,省掉链接步骤。
但更好的方案是:标准库直接支持 syscall()。
因为很多有趣的 libc 函数只是对 syscall() 的包装(open()/write()/ioctl() 等)。如果标准库能直接调用 syscall,就能摆脱对 C 库的依赖。
我不是 Rust 的架构师,也不可能成为。
但我知道,Rust 要成为一门真正成熟的、适合系统开发的编程语言,还缺少一些东西:
| 缺失要素 | 现状 | 理想状态 |
|---|---|---|
| 正式规范 | 参考文档不完整 | ISO C 级别的标准文档 |
| 完全自托管 | 依赖 LLVM,自举链条脆弱 | 真正的自托管编译器 |
| 底层交互 | 依赖 C 库和外部汇编器 | 原生支持 syscall 和汇编 |
这些问题不影响我用 Rust 写项目,但它们决定了 Rust 能走多远。
希望这些问题能够得到解决。
写这些不是为了黑 Rust。
恰恰相反,正是因为我希望 Rust 更好,才愿意花时间指出这些问题。
一门语言成熟的过程,就是不断面对问题、解决问题的过程。
C 语言用了几十年才标准化,C++ 至今还在演进。Rust 还很年轻,有时间,也有机会。
但前提是:社区和团队愿意承认这些问题,并着手解决。
你怎么看 Rust 的这些不足?是"吹毛求疵"还是"切中要害"?欢迎在评论区分享你的观点。
还没有人回复