【 在 littleSram 的大作中提到: 】
: 标 题: [zz]Rust 的不足之处,让它无法成为一门成熟的编程语言
: 发信站: 水木社区 (Sat Sep 24 14:06:26 2022), 站内
:
: 首先,Rust 没有正式的语言规范。我的意思是,尽管对语法和对象等方面进行了解释,但没有正式的规则来描述语言特性可以是什么或不可以是什么。在 ISO C 语言标准里,几乎每一项都有三到四个描述片段:正式的语法约束(即哪些东西是不被允许的或者不能用它完成哪些事情)、语义(
的确,时至今日rust仍然没有正式规范,很多东西只存在于ref和死灵书以及各dev book中,这是个问题。
:
:
: struct Foo { a: i32 }
: impl Foo { fn bar(&mut self, val: i32) { self.a = val + 42; } }
: fn main() {
: let mut foo = Foo { a: 0 };
: foo.bar(foo.a);
: }
:
:
:
: 因为使用了借用,这个无法正常编译,但问题是编译器不是应该“聪明”地在调用之前创建一个 foo.a 的副本吗?我不确定,但 IIRC 的当前实现首先可变地借用对象,然后尝试借用参数。真的是这样吗?如果是,为什么是这样?有人告诉我,新版本编译器处理得很好,但问题仍然存在(
上面这个现在的版本是可以正常编译的。两年前写这个文章的时候可能还没有NLL。
:
:
:
:
: 另一个是关于函数参数求值的警告。这里有一个简单的例子:
:
:
:
: 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.ne
这个现在不再是未定义行为了,ref中已经明确,call expression的求值顺序是从左到右
: 另一个让我抓狂的是 trait。对于我来说,理解所有权、生命周期、借用这些概念都没什么问题,但 trait 几乎每次都会让我抓狂。我隐隐约约地知道这是“因为 trait 被实现成调用表”,但问题是它们应该被这样实现吗?它们的约束是什么?当你有一个超级 trait(例如,trait Foo: B
: ar),你就不可能在不编写大量样板代码的情况下轻易地将它转换成子 trait(例如 &Foo -> &Bar)。
这个的确有点繁琐,但我觉得这是故意的,故意让你少些继承,多用组合,所以把类似继承的写法搞得和组合一样烦甚至更烦
另外,为什么要把父trait转换成子trait?这个做法本来就不靠谱呀
: 更糟糕的是,如果你将一个对象转成 Box,就没有办法找回原来的对象。
Box::downcast
Box::into_inner
Box::into_raw
Box:leak
现在做到这一点的方法已经有很多了
: 重申一下:问题不在于我笨,而在于 Rust 缺乏描述,比如如何实现才是对的、为什么我想要的东西会如此之难。然
: 螅乙馐兜轿倚枰薷淖约旱拇肜慈乒庑┫拗啤
:
: 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,以此类推。
: 当然,这是个理论问题,所以可能会浪费资源,但对编译器设计本身是有好处的。
:
:
:
: 接下来是 LLVM 依赖问题。我知道 LLVM 有很多优点(比如不需要担心在多平台上的代码生成和优化问题),但它也有一些缺点。首先,没有一个真正的自托管编译器(这也是一个理论问题,但仍然值得我们思考)。其次,它的一些行为会限制我们。例如,我看到有很多人抱怨调试构建的速度
: 太慢,主要是因为 LLVM 后端导致的。我猜想它仍然不能做一些与内存相关的优化,因为它的设计参考了 C++编译器,而后者仍然存在奇怪的多内存访问问题。我知道现在有 cranelift,所以希望这个问题可以得到改善。
:
:
:
: 最后,还有一个与上一个问题相关的问题。Rust 对汇编的支撑很差。当然,并不是所有人都需要汇编支持,但面向系统编程的语言除了支持高级语言代码外还应该要支持编译汇编,所以也应该支持汇编文件,即使不像 GAS 那样提供了丰富的预处理器语法。我们可以用 build.rs 来调用外
: 炕惚嗥鳎庋坏阋膊缓谩
:
:
:
: 其他问题
: Rust std 库有一个问题,它对于操作系统的交互来说并没有什么用。如果我想对任意一个 UNIX 系统做一些事情,至少需要导入 libc,并链接到外部 libc(它是运行时的一部分)。一种解决方案是把 musl 翻译成 Rust,这样至少可以省掉链接步骤。但更好的解决方案应该是支持使用 std
: 里的 syscall(),因为很多有趣的 libc 函数只是对 syscall()进行了包装(例如 open()/write()/ioctl())。
:
:
:
: 我不是 Rust 的架构师,也不可能成为 Rust 的架构师。但是我知道,Rust 要成为一门成熟的适合系统开发的编程语言,缺少了一些东西(本质上就是:完全自托管、规范和能够不借助 C 编译器和汇编实现与底层的交互)。希望这些问题能够得到解决
: --
: ※ 修改:·littleSram 于 Sep 24 14:09:37 2022 修改本文·[FROM: 114.249.21.*]
: ※ 来源:·水木社区
http://m.mysmth.net·[FROM: 114.249.21.*]
--
修改:littleSram FROM 114.249.21.*
FROM 123.120.189.*