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

2020 年 10 月 16 日

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

虽然我并不反对 Rust 本身,并且一直在用 Rust 开发自己的项目,但我还是发现它有一些不足之处,让它无法成为一门成熟的编程语言。在这篇文章里,我想把这些问题列出来,并解释为什么我会这样认为,即使它们对我没有任何影响。

Rust 语言本身的问题

首先,Rust 没有正式的语言规范。我的意思是,尽管对语法和对象等方面进行了解释,但没有正式的规则来描述语言特性可以是什么或不可以是什么。在 ISO C 语言标准里,几乎每一项都有三到四个描述片段:正式的语法约束 (即哪些东西是不被允许的或者不能用它完成哪些事情)、语义 (即它可以做什么、它是如何影响程序的、有哪些需要注意的地方),而且可能还会列出一些例子。Rust 参考( https://doc.rust-lang.org/reference/ )中是这样描述结构体的:语法 (没有异议)、类似“结构体是用关键字 struct 定义的名义结构体类型”这样的定义、示例、在示例中间简短地提到空结构体,最后以“结构体没有指定精确的内存占用”结尾。我知道添加新特性比写文档更重要,但这样做确实很蹩脚。

一门成熟的编程语言 (版本到了 1.0) 应该有正式的规范,对于开发编译器的人和使用这门语言的程序员来说都应该有用。例如,对于结构体的定义,我发现至少缺少了这些东西:提到你可以 impl(实现)、将元组拆分成独立的项、说明为什么有匿名的元组而不是匿名的结构体,当然,还要使用适当的布局,让示例中重要的信息 (例如关于内存占用) 不至于丢失。

现在说说我经常遇到的问题,我不知道是我理解错了还是编译器理解错了。而且,由于没有正式的规范,我不知道是哪个出了问题 (即使更有可能是我理解错了)。

调用函数 / 方法。看看这个简单的示例:

复制代码
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 的当前实现首先可变地借用对象,然后尝试借用参数。真的是这样吗?如果是,为什么是这样?有人告诉我,新版本编译器处理得很好,但问题仍然存在 (这是编译器的问题还是调用定义发生变化了?)

另一个是关于函数参数求值的警告。这里有一个简单的例子:

复制代码
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(例如 &Foo -> &Bar)。更糟糕的是,如果你将一个对象转成 Box,就没有办法找回原来的对象。重申一下:问题不在于我笨,而在于 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 编译器和汇编实现与底层的交互)。希望这些问题能够得到解决。

原文链接: https://codecs.multimedia.cx/2020/09/why-rust-is-not-a-mature-programming-language/

2020 年 10 月 16 日 16:07 2

评论

发布
暂无评论
发现更多内容

List 如何一边遍历一边删除

Bruce Duan

List删除元素

架构师第七周 作业

冯凯

ChaosBlade:从零开始的混沌工程(四)

郭旭东

云原生 混沌工程 ChaosBlade

揭秘 vivo 如何打造千万级 DAU 活动中台 - 启航篇

vivo互联网技术

vue.js 中台 Vue 业务中台

第七周总结

晨光

Summary

Kiroro

Week 07 命题作业

Jeremy

脑洞:基于DDD进行组织架构治理

Winfield

组织转型 领域驱动设计 DDD

JVM系列之:Contend注解和false-sharing

程序那些事

Java JVM 性能调优 GC

charles断点使用方法

rainbow

第七周作业

晨光

lambda基本使用

Bruce Duan

Lambda

go defer 的使用和陷阱

曲镇

go

云上自动化 vs 云上编排

华为云开发者社区

云计算 云原生 云服务 云主机 华为云

架构师 第七周总结

冯凯

SQL查找还在用count吗?

Bruce Duan

SQL查询是否存在

新站上线通知

Damon

作业一

Kiroro

SpringBoot教程:MyBatis多数据源配置

Bruce Duan

mybatis SpringBoot 2 多数据源配置

程序设计的一种思路:DDD

再见小飞侠

方法论 DDD 软件设计

阿里取消周报,打击低效加班!HR透露6大原因!

程序员生活志

阿里 周报

架构师Week7作业1

Nan Jiang

一文入门DNS?从访问GitHub开始

Kerwin

后端 DNS DNS服务器

k8s 上运行我们的 springboot 服务之——flume 读取kafka数据批量同步到clickhouse

柠檬

kafka Clickhouse

爱币(LOVE COIN)全球同步耀世上线,掀起币圈追捧热潮

Geek_116789

Java 基础

Bruce Duan

java基础

你在寻找本地部署的后台测试工具吗?

测试那些事儿

一文吃透时间复杂度和空间复杂度

书旅

数据结构 算法 时间复杂度 数据结构与算法

Week 07 学习总结

Jeremy

干货分享丨玩转物联网IoTDA服务系列五-智能家居煤气检测联动

华为云开发者社区

人工智能 物联网 IoT 智能设备 华为云

架构师Week7作业2

Nan Jiang

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