Rust的不足之处,让它无法成为一门成熟的编程语言

虽然我并不反对 Rust 本身 , 并且一直在用 Rust 开发自己的项目 , 但我还是发现它有一些不足之处 , 让它无法成为一门成熟的编程语言 。 在这篇文章里 , 我想把这些问题列出来 , 并解释为什么我会这样认为 , 即使它们对我没有任何影响 。
Rust 语言本身的问题首先 , Rust 没有正式的语言规范 。 我的意思是 , 尽管对语法和对象等方面进行了解释 , 但没有正式的规则来描述语言特性可以是什么或不可以是什么 。 在 ISO C 语言标准里 , 几乎每一项都有三到四个描述片段:正式的语法约束 (即哪些东西是不被允许的或者不能用它完成哪些事情)、语义 (即它可以做什么、它是如何影响程序的、有哪些需要注意的地方) , 而且可能还会列出一些例子 。 Rust 参考()中是这样描述结构体的:语法 (没有异议)、类似“结构体是用关键字 struct 定义的名义结构体类型”这样的定义、示例、在示例中间简短地提到空结构体 , 最后以“结构体没有指定精确的内存占用”结尾 。 我知道添加新特性比写文档更重要 , 但这样做确实很蹩脚 。
一门成熟的编程语言 (版本到了 1.0) 应该有正式的规范 , 对于开发编译器的人和使用这门语言的程序员来说都应该有用 。 例如 , 对于结构体的定义 , 我发现至少缺少了这些东西:提到你可以 impl(实现)、将元组拆分成独立的项、说明为什么有匿名的元组而不是匿名的结构体 , 当然 , 还要使用适当的布局 , 让示例中重要的信息 (例如关于内存占用) 不至于丢失 。
现在说说我经常遇到的问题 , 我不知道是我理解错了还是编译器理解错了 。 而且 , 由于没有正式的规范 , 我不知道是哪个出了问题 (即使更有可能是我理解错了) 。
调用函数 / 方法 。 看看这个简单的示例:
struct Foo { a: i32 }impl Foo { fn bar( } }fn main() {let mut foo = Foo { a: 0 };foo.bar(foo.a);}因为使用了借用 , 这个无法正常编译 , 但问题是编译器不是应该“聪明”地在调用之前创建一个 foo.a 的副本吗?我不确定 , 但 IIRC 的当前实现首先可变地借用对象 , 然后尝试借用参数 。 真的是这样吗?如果是 , 为什么是这样?有人告诉我 , 新版本编译器处理得很好 , 但问题仍然存在 (这是编译器的问题还是调用定义发生变化了?)
另一个是关于函数参数求值的警告 。 这里有一个简单的例子:
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 , 因为没有正式的规范告诉你它应该是怎样的 。 如果你要通过索引访问调用者对象 , 比如 handler[iter.next().unwrap() as usize].process(iter.next().unwrap()) , 情况会更糟糕 。
另一个让我抓狂的是 trait 。 对于我来说 , 理解所有权、生命周期、借用这些概念都没什么问题 , 但 trait 几乎每次都会让我抓狂 。 我隐隐约约地知道这是“因为 trait 被实现成调用表” , 但问题是它们应该被这样实现吗?它们的约束是什么?当你有一个超级 trait(例如 , trait Foo: Bar) , 你就不可能在不编写大量样板代码的情况下轻易地将它转换成子 trait(例如--tt-darkmode-color: #A3A3A3;">rustc 的问题其实我不是想讨论编译速度问题 , 尽管有点扰人 , 但它本身并不是个问题 。 我想指出的是一些一门成熟的编程语言不应该有的问题 , 而一门语言只有一个编译器就是其中的一个问题 。
首先 , 自举过程非常糟糕 。 我知道这并不容易 , 但如果 Rust 被认为是一门系统编程语言 , 那么就应该能够通过几个步骤来自举编译器 。 例如 , IIRC Guix 对 C 编译器的自举过程:用简单 C 编译器 (通常可以通过手动编写汇编代码实现) 编译 TCC , 用 TCC 编译 GCC 2.95 , 用 GCC 2.95 编译 GCC 3.7 , 用 GCC 3.7 编译 GCC 4.9 。 对于 rustc 来说 , 你要么使用原始的编译器(使用 OCaml 开发的) , 然后使用前一个版本编译后一个版本 (即使用 1.16 编译 1.17) , 要么使用 mrustc(使用 C++ 开发的) , 它可以编译 1.19 或 1.29(没有借用检查) , 然后使用 1.29 编译 1.30 , 使用 1.30 编译 1.31 , 以此类推 。 这里的问题是 , 你不能跳过版本 , 比如用 rustc 1.36 编译 rustc 1.46 。 在我看来 , 你应该有一种编译器 , 效率可以不高 , 但要用一种老编译器能够理解的方言来开发 , 即使用 rustc 1.0 编译 1.10 的编译器 , 这个编译器可以用来编译 1.20 , 以此类推 。 当然 , 这是个理论问题 , 所以可能会浪费资源 , 但对编译器设计本身是有好处的 。