【外评】为什么我希望不要让 Rust 锈化一切?
让我先声明,我认为 Rust 是整个行业向前迈出的一大步。它很棒,如果你喜欢,那就太好了。如果它适合手头的工作,那就太好了。继续使用 Rust 吧。
这篇文章是关于其他方面的。因为我希望 Rust 并不是什么都能用。
好吧,很明显,它不会被用于所有领域,但我的意思是,我希望 Rust 不会成为系统编程和/或应用编程的唯一选择,因为我讨厌用 Rust 写作,但我喜欢应用和系统编程。
当然,如果人们用 Rust 写东西,我也没意见。但如果你强迫我使用 Rust,无论是通过法律、羞辱还是取消或饿死其他语言,那么我就会选择 firebird。Dishonor on you! Dishonor on your cow!
别让 Rust 感染一切。不要让编程行业的文化转向 Rust 或毁灭。如果你纯粹以是否使用 Rust 编写来选择其他程序,那你就是问题所在。
Rust 不会自动让事情变得更好。它应该用在能让事情变得更好的地方,但不能超出这个范围。
换句话说,编程语言的单一化对我们的行业来说将是灾难性的。不要让它发生。
好了,发帖结束。我已经说了重要的部分。
是时候咆哮了。如果你愿意,现在就离开吧。
…
我为什么讨厌 Rust?
首先,语法。太丑了 对我的眼睛来说 放下焦油 不止我一个人这么想!
语法是编程语言的用户界面,而我更喜欢不那么重符号的语言。
第二,异步断裂。非同步 Rust 本质上是二等公民。这对我来说很糟糕,因为我不喜欢 async。我就是不喜欢,我的脑袋不是为它而生的。
第三,第二种 async 裂缝。如果你正在使用 async,那么你可能正在使用 tokio。尽管 Rust 承诺 async 与执行器无关,但 Crates 支持 tokio,但不支持其他执行器(如 smol)。
我喜欢依赖性少的小程序。为了避免依赖,我会自己编写 I/O。对于依赖关系来说,Tokio 就像 god objects 对于面向对象编程一样。
第四,Rust 很复杂!这主要是因为 async。
尽管我没有 Rust 的资历,但我读过 without.boats 和 Baby Steps,每当他们开始谈论 async Rust 时,我就是无法理解。别忘了,我可是把异步 Zig 挑得体无完肤!
人们说 C 语言很复杂,其实 C 语言很简单。人们说在 C 语言中很难避免 UB,这没错,但有一个不完整的列表(C23 会有一个更全面的列表)。实际上,我可以阅读并理解整个标准。
对于 async Rust,本质上就是 “rustc
做什么,它就做什么”,这并不比一个黑盒子好多少。即使 Rust 规范出台,我怀疑它也会和 C++ 规范一样长,而且大部分内容都会以某种方式与 async 有关(除去定义标准库的内容)。
听着,我理解生命周期和shared^mut
;我在 C 语言中实现了它们的一种形式。但我就是不理解异步 Rust。
如果 Rust 的设计者没有试图设计出一种能满足所有人需求的语言,这本来是可以避免的。他们试图允许多个执行器,并试图获得最高性能。是的,他们在这两个目标上取得了成功,但当这些目标是以一种不合理的编程语言为代价时,我认为它们就变成了错误的目标。
第五,尽管如此复杂,async Rust 仍然是病毒式的。
第六,Rust 在静态分析方面走得不够远。Rust 有机会走得更远,但团队搞砸了。
第七,Rust 的编译时间是个笑话。是的,我就直说了。
我运行的是 Gentoo。Gentoo 从源代码编译。
当我在过期软件包列表中看到一个 Rust 项目时,我就会叹口气,只有在快睡觉的时候才会发送编译。
在浏览器方面,这对我来说是可以接受的。是的,这个庞然大物应该使用 Rust,我很高兴它这么做了。还有其他的。
但有些 Rust 软件包让我相信魔鬼的存在。
我要特别指出一个: Starship。
Starship 很不错。我喜欢它运行时的样子。
但在构建过程中,我感觉自己就像没吃药的豪斯医生。
Gentoo 运行发布版构建。你猜需要多长时间?
如果你说是 15 分钟,那你说对了,但那只是构建 main.rs
文件!lib.rs
文件还需要 10 分钟左右,更不用说其他文件了。
相比之下,我的 monorepo 只需不到 30 秒就能完成整个发布版本的构建。
“听起来不太好,加文”。
啊,是我的错,我的意思是,它能在 30 秒内完成完整的引导。
monorepo 包含自己的构建系统,因此需要对其进行引导。它也不只是做最小的引导;它会在开发构建中用 “调试代码 ”构建一次完整的系统,然后用发布构建再构建一次,最后用发布构建再构建第三次。
第一次构建很简单,应该很快,而且确实很快。它使用一个简单的 C 程序和硬编码文件列表,从头开始构建。
但它构建的版本有我所说的 “调试代码”。
“那只是断言吗,加文?”
是的,它包括断言,但这并不是它的全部。
我有专门的代码来检查前置条件、后置条件和不变式。其中一些代码非常彻底,彻底到通常是 O(n)
,甚至是 O(n^2)
。
我的构建系统在目标数量上基本上是 O(n)
,但当它编译了调试代码后,整个系统就变成了 O(n^2)
。
这就是运行第二阶段引导的原因。在没有优化的开发构建中。
一个完整的发布版构建,仅仅是一个发布版构建,同时使用一个发布版构建,大约需要 8 秒钟。即使我们把时间加倍到 16 秒(因为有一个发布版构建和一个开发版构建),中间的构建也只需要 15 秒左右。
对于 193 个文件。
我已经说过 Starship 对于发布版构建有多糟糕,那么开发版构建呢?
在 Starship 上运行一个干净的开发构建需要 1:30。
“这还不算太糟……”
你先说 30 秒的完整引导构建不算太好,然后又这么说?
但无论如何,与我的 monorepo 上 4 秒钟的简洁开发构建相比,90 秒并不算好。那是 20 多倍的时间!
那么增量构建呢?只修改一个 Starship 文件(main.rs
)就需要 8.8 秒。在我的 monorepo 中修改一个文件,只需 0.5 秒。还是长了 17 倍。
我还没提到最糟糕的部分: 我滥用了预处理器。
我有一个文件,include/yc/arith.h
,使用宏来生成安全的算术函数。
我修改的文件 src/urdo/main.c
大约有 2300 行。但它包含了 arith.h
,因此扩展到超过 27k LoC 和将近一兆字节。相比之下,Starship 的 main.rs
只有 273 行和 9k 字节。
是的,C++ 的扩展要大得多,是的,我没有使用外部库。
“加文,Rust 有宏之类的东西,让它变得强大。而宏可以扩展大量代码。”
得了吧 你们应该把 C++ 当成一个警告,而不是一个例子。
好吧,宏给你带来了强大的功能,但只要停下来想一想:真的没有办法在不需要 xkcd 编译的情况下就拥有如此强大的功能吗?
事实是:办法是有的。
我的构建系统使用了一种我自己设计的语言,叫做 “Yao”。Yao 可以让客户添加关键字。它还能让客户更改某些代码的词法。
有了它,我就能实现特殊的关键字。Rig 使用一个关键字来定义目标:
target stuff.o: stuff.c
{
$ clang $CFLAGS -o stuff.o stuff.c;
}
看到以 $
开头的那一行了吗? 那是 shell 调用。 这就是更改词法后的结果。 我甚至可以嵌入 JSON:
j := @(json){
"key1": "data",
"key2": 1.0,
"key3": true
};
所以说,Yao 和 Rust 一样强大。
“加文,我敢打赌,你在自己的 Yao 代码中使用的关键字/宏并不多”。
事实上,我用了。Yao 的基元为零;甚至 if
、while
和 foreach
也是作为用户自定义关键字实现的。每个关键字都是,包括 return
和 fn
(定义函数)。我使用了大量的 “宏”,因为根本无法避免它们。
然而,Yao 的编译速度很快。
有多快?在 Starship 上进行空编译(无事可做)只需 0.2 秒。在我的 monorepo 上进行cargo build
只需 0.1 秒。编译并运行 1800 行 Yao 代码。这还不包括解析另外三个配置文件,这些文件甚至都不在 Yao 中。
除了调试代码中的断言外,这些数字还包括所有断言。
我的最终目标是,Yao 可以每秒编译 100 万 LoC。有些语言能做到这一点,因为计算机的速度很快。
100 万 LoC/s 有多快?你可以在一个 10k LoC 文件上以每分钟 120 次(720 cpm)的速度编译每个按键,而且每个按键之间还有一点空闲时间!
嗯……如果编译器可以在每次击键时进行编译,那么是否也可以在编译器中添加对语言服务器协议(LSP)的支持,并将编译器用作 LSP 服务器?
当然可以!
我的 Yao 编译器已经为每个标记设置了一些数据;LSP 服务器只需遍历生成的标记列表,然后将信息发送到客户端即可。编译错误也是正确的,因为是编译器产生了这些错误!
换句话说,如果编译速度快,工具就很容易。
而 Rust 则必须有一个单独的懒编译器来支持 LSP,这个编译器复杂到需要一个 21 视频播放列表来解释它的架构。
Rust: 不,编译器不能成为 LSP 服务器!Yao :哈哈,编译器去吧
即使是比较懂行的人也会犯这样的错误!
Turbo Pascal 的创建者 Anders Hejlsberg 对如何制作快速编译器以及人们对它们的喜爱程度应该略知一二,他在 Microsoft 制作了一个视频,讨论如何让编译器变得懒惰,以使其具有足够的响应能力。
好吧,如果他让 C# 的编译速度达到 1M LoC/s,就没必要这样了。
“但是加文 你需要让编译器变得懒惰,这样它们才能处理不完整的代码!”
不,不需要。Clang 和 GCC 会返回多个错误,因此它们已经需要在面对错误时保持稳健。Yao 的编译器也会返回多个错误。
总之,咆哮结束了。
别让 Rust 把一切都锈化了。单一文化不好。
本文文字及图片出自 Why I Hope Rust Does Not Oxidize Everything
你也许感兴趣的:
- Android 全力押注 Rust,Linux 却在原地踏步?谷歌:用 Rust 重写固件太简单了!
- C 语言老将从中作梗,Rust for Linux 项目内讧升级!核心维护者愤然离职:不受尊重、热情被消耗光
- 从电梯故障到编程新宠,Rust为何连续七年称霸「最受推崇语言」
- 【外评】不要把 Rust 写成 Java
- 语言设计: Rust 的几乎规则
- 美国国防部建议将C代码转换为Rust
- 【外评】Why Not Rust?
- 【外评】 我使用(并喜爱)Rust 已经有 10 年了, 以下是它让我失望的地方
- 【外评】Rust 版的 Linux 文件系统
- Vue诞生10年,创始人尤雨溪推动“锈化”——通过Rust提升Web基础设施性能
作者似乎非常焦虑,因为 Rust 正在受到重视,而他们并不喜欢 Rust。他们担心有一天 Rust 会成为一种 “单一文化”,所有东西都用它来写。
我喜欢 Rust,但我认为这种可能性非常非常小。
Rust 实际上为编程语言带来了更多选择。如果要谈单一语言,那就谈谈 C/C++。几十年来,这是系统编程的唯一可行选择。所有新语言都专注于更高层次。低层次的语言很少见。虽然有 D 语言,但它从未获得足够的关注。
后来,Rust 出现了,终于有了另一种选择。不仅如此,正因为如此,其他语言设计者也决定创建新的系统语言,现在我们有了 Zig、Odin 和 Vale 等。
所以说,Rust 是在帮助打破单一文化,而不是在创造单一文化。C 和 C++ 不会消失,但现在我们有了替代品。
我认为重要的是要承认,即使你不喜欢某种语言,但如果你看到许多软件都是用这种语言编写的,那是因为这种语言很有用。我不喜欢 C++,但我承认它非常有用!人们用 Rust 写出有趣的软件,是因为他们觉得它有用。
Rust之所以向人们提出挑战,是因为它宣布了C/C++长期以来的一些不足之处(安全问题、依赖管理),并提供了替代方案,表明不一定非得如此。
改写的过程将不可避免地漫长而痛苦。重写总是如此。但反 Rust 的人们现在的责任是先展示出一种更好的语言来重写,而不是坐等现状,等着螃蟹驾驶的蒸汽机慢慢地碾过他们。
D 语言很有趣,但似乎是一个单打独斗的项目,我不知道为什么它没有受到重视。也许它还不够与众不同。
这并不是对你的批评,但你的评论中隐约透露出一些 Rust 传教士让我感到困扰的地方。我称之为 “略带得意的过度乐观”。
我有一个在生产中运行的 C++ 服务。它已经运行了 10 来年,更新很少。在接下来的 10 年里,它可能会一直运行得很好。
考虑到这一点,“反 Rust 的人现在的责任是先展示出一种更好的语言来重写,而不是坐等现状,等着螃蟹驾驶的蒸汽机慢慢碾过他们 ”在我看来没有什么意义。如果现状没问题,就没有 “责任”,就不需要做出艰难的决定,就不需要重写。反 Rust 的人可能什么都不做……就没事了。
> 我有一个在生产中运行的 C++ 服务
它处于网络(或其他)安全边界上,会受到来自互联网的攻击吗?
是否部署在全球数百万台机器上?
这些是主要的替换目标,因为网络环境是一个充满敌意的地方,而人们已经厌倦了这样的后果。定期发布 “对不起,您的私人数据已被泄露,笑 ”的公告。不断升级以修复最新的 CVE。
(这一点的典型代表就是后来被 Adobe 收购的 Shockwave Flash,它存在大量 RCE 漏洞,所有人都联合起来支持苹果公司将其关闭。即使这意味着依赖于它的媒体和游戏的整个时代都将被淘汰。这并没有被重写,只是被扼杀了而已)。
我猜大多数对性能有影响的服务都在后台。而这种特殊的 C++ 服务只能通过内部局域网访问,供其他后端服务器使用。
我同意你的观点,如果 ha-proxy 和 nginx 还不存在,它们将是用 Rust 实现的首要候选。但既然它们已经存在并能可靠地工作,我不确定它们是否会很快被取代。顺便提一下,上一次 ha-proxy CVE 就是因为它们与 HTTP 规范不同,在额外的 URL 组件中接受了 # 字符,这可能是任何编译器都无法识别的。
> 我不确定是否有足够的痛苦让它们很快被替换。
https://blog.cloudflare.com/how-we-built-pingora-the-proxy-t…
https://blog.cloudflare.com/pingora-open-source
这听起来是个不错的项目:)但是
1. “Pingora 不是 Nginx 的替代品” https://navendu.me/posts/pingora/
2. 你还是把它放在 ha-proxy 后面 https://github.com/cloudflare/pingora/issues/132
3. https://github.com/cloudflare/pingora 说 “Pingora 保持 6 个月的 MSRV(最小支持 Rust 版本)滚动政策”,因此对于不喜欢 “不断升级的跑步机 ”的人来说,这不会有太大帮助。
因此,我的总结是,Pingora 是一个很棒的 Rust 库,也许有一天会用来替代 nginx 和/或 ha-proxy。
但是,Pingora 的主要优势(这也是 CloudFlare 现在使用它的原因)与 Rust 无关。显然,2022 年设计的软件架构可以利用现代硬件,而 2004 年设计的架构则无法做到这一点。(是的,nginx 就是这么老)。大约 10 年前,英特尔的 TBB 库为 C++ 带来了 “偷工减料”。Pingora 的另一大改进是从多进程转向多线程池。同样,C++ 拥有线程池已有多年。
所以,Pingora 可能很棒,而且是用 Rust 写的。但它带来的商业利益并非来自 Rust。而是因为它是一种现代架构。
> 但它带来的商业利益并非来自 Rust。而是因为它是一种现代架构。
这是转移目标。你的原帖说
> 我不确定是否有足够的痛苦让它们很快被取代。
然而,在这里,他们中的一个人却被一家公司取代,而这家公司的业务占互联网流量的 10%。
除此之外,您的链接
> 1. “Pingora 不是 Nginx 的替代品” https://navendu.me/posts/pingora/
下面是文章开头的实际内容:
> 把Pingora想象成一个引擎,它可以为汽车提供动力,而你却必须自己造车。Nginx 是一辆完整的汽车,你可以驾驶它。River 是一辆更快、更安全、更容易定制的汽车。
标题是迂腐的效果。它并没有像你说的那样。
> 2.你还是把它放在了 HA-PROXY https://github.com/cloudflare/pingora/issues/132 后面。
这是某个人在开源软件源上提出的问题。他们不是在讨论 Cloudflare 本身如何使用它,而是在讨论他们希望如何使用它。
> 因此,对于任何不喜欢 “不断升级的跑步机 ”的人来说,这不会有太大帮助。
与上文类似,这也是在转移目标。当然,这可能是事实,但与最初的话题无关。
> 但 Pingora 的主要优势–这也是 CloudFlare 现在使用它的原因–与 Rust 无关。
Cloudflare 自己可不是这么说的。他们在构建 Pingora 时选择 Rust 是出于非常特殊的原因:(重复上面的链接)https://blog.cloudflare.com/how-we-built-pingora-the-proxy-t…
> 我们之所以选择 Rust 作为项目语言,是因为它可以在不影响性能的前提下,以内存安全的方式完成 C 语言所能完成的工作。
多年来,Cloudflare 一直是 Rust 的积极支持者。很多年前,他们遭遇了一个非常严重的 bug CloudBleed,而 Rust 本可以避免这个 bug。因此他们很早就开始使用 Rust 来代替 C 和 C++。
当然,他们也非常同意架构很重要,但这并不意味着实现语言也不重要。如果他们选择用 Ruby 来实现 Pingora,那就无法实现他们的目标。
我不认为有人会相信未来会没有 C++ 代码库,那是痴人说梦。十几二十年后可能出现的情况是,没有新的 C++ 代码库。流行语言时不时就会退出历史舞台,而 C++ 已经被 Rust 完全超越。
人们总以为 C++ 只用于系统编程,但事实上 C++ 无处不在。
FORTRAN 仍在不断发展和改进,新代码(尤其是科学领域的新代码)仍在不断编写。
Rust 对 C++ 的影响就像 clang 对 GCC 的影响一样。唤醒巨人吧 Rust 将一事无成,但对 C++ 也是一样。
认为 C++ 将消声匿迹只是一厢情愿的想法。
有哪些新的科学应用是用 Fortran 编写的?
由于 C++ 仍在不断发展和变化,我想未来的新建 C++ 项目可以将自己限制在较新的改进语言的子集中,从而使 C++ 也能以这种方式继续生存下去。
C++ 社区正在讨论在这个领域以以下方式发展:
“语言子集 “的概念经常被提及,但似乎有人明确反对子集的概念,至少委员会的几位重要成员是这样认为的。我不清楚为什么会这样。
我不明白你为什么会被降权。委员会成员显然希望保持现状。不过,从实用的角度来看,我怀疑 “子集朗读 ”会发生。我们只需要有一个衬底或编译器标志就可以做到这一点。
没关系,反正我的业力也太多了。
问题是,真正的子集与 “一些拒绝某些东西的标志 ”是有区别的。因为这会产生许多不同的集合,这些集合可能以各种方式相互关联,有些是子集,有些是重叠的。
但除了这里的具体定义之外,“配置文件 ”就是这样一种方法,所以类似的事情很可能会发生。它似乎得到了很多人的支持。
“子集语言 “已经是一个选项。开发者可以选择一个安全的 C++ 子集来约束自己。许多(大多数?)C++ 商店已经在这样做了,而且它们会采取不同的措施,确保只使用它们祝福的子集。我们并不需要一个委员会来创建一个新的 “子集语言 ”来实现这一目标。
C++ 在 C++17 和更新的迭代版本中已经超越了这一界限。从 C++17 开始,C++ 不仅发展得更快,而且现代代码看起来也大不相同,需要从头开始重新学习 C++ 的某些部分。
我曾用 C++11 编写过我最大的项目,当时 C++14 刚刚问世。现在,我计划用 C++ 重新实现那个项目(并对其进行改进),但我需要学习所谓的 “现代 C++”,以便以一种更面向未来的方式正确实现它。
……我很高兴我必须这么做,因为虽然我喜欢(老式)C++,但看到它的发展让我很高兴。因为系统在进化,软件规模在进化,最重要的是硬件和获得最高性能的方法也在进化。
没有必要再写老派的 C++ 了。所有这些功能的开发都是有原因的,它们应该被使用。
我也有同感。我喜欢 Rust,但我看不到 Rust 的单一文化。
从我的经验来看,Rust 在很多角落都绝对能改善开发者的体验。我期待着未来 Rust 的成熟和无趣,它所有的圆边都已解决,即使这意味着要采用另一种新的令人兴奋的语言:)
在我看来,只有一个短暂的单一文化时期,而且只有当你将 C 和 C++ 视为同一文化的一部分时才会出现,而这是一种牵强附会。从 80 年代中期开始,人们不再使用 Pascal 和/或汇编语言编写程序,到 90 年代中期 Java 和 Perl 开始广泛使用,这种情况就结束了。
> 如果你认为 C 和 C++ 是同一种文化的一部分,那就太夸张了。
老实说,这是我对大多数支持 Rust 的此类评论的主要不满。写 C 的方法有几种,写 C++ 的方法有很多,而且大多数 C 与大多数 C++ 都很不一样。(在这里,我并没有把原始 GObject 或原始 COM 等边缘情况也算作 C,我认为这些基本上都是独立的语言)。
我并不讨厌 Rust 背后的理论–我写过 Haskell,写过 SML,十多年前就读过 Tofte&Talpin regions 的论文和一些后续研究。我不喜欢别人要求我改用 C++,甚至试图羞辱我,让我从 C 语言改用自认为更好的 C++,就像我不喜欢有人试图让我改用 C++,却声称 C++ 和 C 语言是一回事一样。而且,当我读到 “C/C++”时,我基本上就不会再读了,因为这意味着作者并不了解它。
(我知道还有其他人明白这一点,其中一些人还在研究其他编程语言。他们只是不会写文章建议用 Rust 取代 C 语言)。
我说的是系统编程领域的单一文化。Java、Perl、PhP、Python、Ruby、JavaScript、C#、Go,这些语言很流行,但它们都使用垃圾回收,在系统编程方面有局限性。在很长一段时间里,C 和 C++ 是唯一的选择。
系统编程只是一个小众领域。一个大的利基市场,但只是一个利基市场。但在 Pascal 和汇编语言停止广泛使用到 Java 开始广泛使用之前,C 和 C++ 几乎被用于所有领域。
顺便说一句,正是 LLVM 催化了现代编程语言的创新。Rust 只是这一转变的另一个结果,而不是原因。
在链接的帖子[0]中,作者为 Rust 做了最好的论证
> Rust 非常适合团队使用,因为它去除了许多让团队代码工作变得危险的东西。上面提到的就是其中一些。不过,我喜欢独自工作,因为我喜欢把代码保存在自己的头脑中,而与人合作意味着代码的一部分只能保存在他们的头脑中。 这也意味着语言越大,我脑子里的代码空间就越小。 不幸的是,Rust 对我的小脑袋来说太大了。
大多数代码都是团队编写的,即使是只有一个作者的软件,也需要为维护者下台时制定计划。除此之外,我不同意作者关于 Rust 不适合他们的断言,事实上,我认为 Rust 严格来说比 C 语言更好,因为它通常不需要把东西记在脑子里。就拿生命周期来说,在 C 语言中,你必须在脑中记录指针及其生命周期,而且顾名思义,所有其他维护者也必须这样做,而在 Rust 中,编译器会为你做这些事情,减少了在脑中记录的麻烦。事实上,我认为把事情从脑子里卸载给编译器是 Rust 的一大优势。这也与编译时间有关,是的,Rust 的编译速度非常慢,但编译器所做的工作也比 C 编译器多得多。很显然,我们希望 rustc 的编译速度越快越好,但它永远比不上 C 或 Go 编译器。
0: https://gavinhoward.com/2023/02/why-i-use-c-when-i-believe-i…
“大多数代码都是团队编写的,即使是只有一个作者的软件,也需要为维护者下台制定计划”。
我坚决不同意!我的经验是这样的
1. 单个开发人员构建有用的东西
2. 团队接手维护
3. 功能膨胀、运行迟缓、错误被忽视、敏捷计划、“产品所有者” 4.
4. 公司破产或被收购
我认为,如果你和一个大团队一起工作,你就不可能有一个人的清晰视野。这就是为什么许多有用的东西都是从一个人或一个很小的团队开始的,比如 Linux、Android、curl、ffmpeg、Quake 引擎、2 人的 Firefox “凤凰 ”团队、最初的 3 人 JetBrains 团队、最初的 2 个 Photoshop 开发人员。
> 这就是为什么许多有用的东西都是从一个人或*个很小的团队*开始的、
你在这里巧妙地转移了目标。但是,即使一开始只有一个人,最终也必须有新的维护者,这仅仅是因为人类的死亡率。软件是一个年轻的领域,所以我们还不太需要面对这个事实,但这是不可避免的。
我个人认为,像 Rust 这样的语言对大型单机程序也很有帮助,把所有东西都记在脑子里超过 10kCLOC 是很困难的,所以为什么不使用一种能帮助你的语言和编译器呢?
我个人的看法是,Rust 有点过于冗长,无法将所有东西都记在脑子里。如果是单独编写程序,我可能会首先选择 Python,因为它往往能生成非常简短但可读的源代码。
是的,我假设团队的规模为中型到大型,这有点偏离了目标。如果我假设一个非常小的团队,就像最初发布 Photoshop 时只有两个人每天紧密合作,那么我认为他们的沟通会非常好,不会再有 “很多让团队代码工作变得危险的事情”。所以在这种情况下,我不会指望使用 Rust 能带来多少好处。
> 我个人的看法是,Rust 有点过于冗长,无法把所有东西都记在脑子里
我很好奇你说的 “冗长 ”是什么意思。我之所以说 Rust 能让我把更少的东西记在脑子里,是因为在 C 语言中像这样的东西:
如果不清楚 Foo 是否拥有 Bar 或者只是比它活得更久,则变为
在 Rust 中,我再也不用把这些信息记在脑子里了。
有趣的是,我脑子里大多是概念和架构,而代码行数较少。
唉,这种情况在小公司里发生过很多次。O.G. 的代码库是由一个人编写的。它能很好地完成一两件事,错误很少,文件名、变量名、应用程序接口、格式化等都很一致,整个代码只有一个纯粹的愿景,不会出现多人对同一代码进行不同推理时出现的所有问题。
后来,公司规模越来越大,产品的发展需要超出一个人的能力,于是一个团队开始开发代码库。这时,错误就会开始涌现,一切都变得不那么一致,不同的设计模式被广泛使用,“单一清晰的愿景 ”也会被抛到一边。很多被归咎于 “技术债务 ”和 “脆性代码 ”的问题,往往只是因为多人无法共享一个意识而导致的简单的协调和一致性问题。
Rust 当然不是系统编程语言中的最后一种,但与 C 和 C++ 相比,它是一个巨大的飞跃。老实说,我们的行业花了这么长的时间才将基本的安全性和 1975 年左右的 ML 语言特性融入到这一利基市场中,这不能不说是一种悲哀。如果情况稍有不同,我们可能在过去几十年中一直在使用 OCaml。
我最喜欢的假设是,编程语言爱好者太与众不同了。他们无法说服普通程序员相信自己喜爱的语言的优点,因为他们的思维方式与众不同。他们认为有说服力的论据对普通程序员来说没有说服力。
进步是循序渐进的。现有语言会增加新功能。新语言会流行起来,因为它们看起来很熟悉,却包含一些有意义的小改进。熟悉的感觉很重要。像 OCaml 这样的语言永远不会流行起来,因为它们太与众不同了。甚至连语法看起来都很陌生。
> 他们认为有说服力的论据对普通程序员来说并不具有说服力
我认为其中一个问题是,人们不相信存在 “普通程序员”。用平均值(或中位数)来思考问题很容易,但现实是没有平均值的人,也没有中位数的人。
认识到每个人都是不同的这一点很重要,因为这样你就能在更多的利基市场中开展工作。与 “用 Y 语言编写 X 的程序员 ”相比,“用 Y 语言编写 X 的程序员 ”更容易吸引 “用 Y 语言编写 X 的程序员”,而 “用 Y 语言编写 X 的程序员 ”更容易吸引 “用 Y 语言编写 X 的程序员”。
或者说,用说服服务开发者放弃 C++ 改用 Rust 的论据来说服游戏开发者放弃 C++ 改用 Rust,你永远也说服不了游戏开发者。
我同意这一点,但一个大问题是,在某些情况下,不打破向后兼容性就无法取得进展。如果要解决的问题是允许某些不该允许的东西,那么向后兼容性就会阻碍进步。
C 和 C++ 允许许多不应该允许的东西来实现它们的目标。它们永远不会停止允许这些事情,因此它们永远不会在某些方面有所改进。
Rust 有很强的向后兼容性保证,它的标准库已经遇到了这个问题。版本系统意味着语言可以在某些情况下打破兼容性,但标准库却不能。我怀疑这最终会阻碍必要的增量变化,就像 C 和 C++ 一样。
我想你是对的。就像人们对 Systemd、MacOS、KDE、Gnome、I3、Nix、Debian、Gentoo、Arch 等有强烈偏好一样。
有这么多不同的解决方案。大多数都是由于主观原因(虽然有时只是推理能力差),因为人们的想法大相径庭。
> 现有语言获得新功能
C++ 就是这样诞生的
或者英语。
过去,很多人都主张在国际场合和重要事务交流中使用自己喜欢的古代语言/人工语言。但不知何故,自然语言 C++ 成了主流。
由于GC的原因,人们拒绝在这些用例中使用OCaml,但我不知道这在实际应用中是否重要。可以应用于 ML 语言的优化(例如 MLTon)非常先进,而且轻松实现多核的能力(我知道这对 OCaml 来说是最近才有的)可以大大提高性能,这在 C 语言中是很难做到的。
GC 确实很重要。
大多数应用程序完全可以使用垃圾回收器,但不能使用多个垃圾回收器!混合使用多种垃圾回收语言会导致复杂的错误。至少,跨语言的引用循环会导致内存泄漏。
因此,每一种垃圾收集语言都会发展出自己的库生态系统,整个世界都必须用该语言重新实现。唯一的例外是 C/C++/Rust 库,因为几乎所有其他语言都可以(通过 C FFI 接口)使用这些库。这正是因为这些语言没有引入自己的重量级运行时(尤其是:没有 GC),允许它们与其他语言的运行时相结合。
因此,被广泛使用的库必须使用其中一种非 GC 语言编写,即使使用这些库的应用程序 100% 都可以使用车库收集器。
我看到异步执行器也出现了同样的情况。Tokio 正在取代异步执行器,但它与其他异步执行器的配合并不好。我刚写了一个混合使用 tokio 和 glib 的程序,结果并不好玩。
我觉得这确实是一个可以解决的问题,所以我希望他们能解决这个问题。我认为第一步可能是像 without.boats 提议的那样,在标准库中添加类似 https://github.com/zesterer/pollster 的东西,以建立最小公分母。
对于约 99% 的应用程序来说,GC 是完全没问题的,这也是为什么过去几十年来所有语言都采用了 GC,并使自己不再是 C/C++ 的直接竞争对手。
缺乏 GC 并不是大多数程序的要求。然而,对于一种要真正取代 C 和 C++ 的语言来说,这却是一个必要条件,因为这两种语言主要用于剩余的部分程序和库中,在这些程序和库中,任何 GC 或任何胖运行时都是不可取的。即使某些 GC 可以运行,也很难让 C/C++ 用户接受。
> 这是我们行业的悲哀,因为我们花了这么长时间才将基本的安全性和 1975 年左右的 ML 语言特性引入到这个利基市场。
确实如此。会不会是因为我们对每一种编程语言、每一种框架和堆栈都吹毛求疵,以至于需要花时间才能取得进展?某种瘫痪、不安全感和困惑?
可悲的是,Rust 只是在某些用例上实现了 “相对于 C 和 C++ 的巨大飞跃”。
与 C 语言相比,Rust 中的源代码量是 C 语言的 10 倍,而嵌入式系统的所有标准库都是 C 语言的,因此要进入嵌入式开发领域,你肯定需要擅长阅读 C 语言代码。Rust 是一项额外的技能,但如果你只懂 Rust 而不懂 C,那么你在嵌入式领域就毫无用处。
我认为这就是我们不放弃 C 语言的主要原因:如果你想成为一名有能力的高级开发人员,你仍然需要牢固掌握 C 语言。
于是,C++ 就顺理成章地升级了,因为它与 C 兼容,而且看起来非常相似。Java 也做得很好,因为它看起来和感觉非常相似。而且还有很好的 C 到 Java 的自动转换器。因此,与 C 相比,Java 的优势几乎是免费的。另一方面,Rust 扔掉了大部分 C 语言的约定,这让人感觉陌生和 “不兼容”。
在我看来,Rust 还存在一个严重的 “不是在这里发明的 ”问题。在 C++ 或 Java 中重用 C 代码非常容易。而在 Rust 中重用 C 代码则要困难得多,除非你采用不安全的方式,放弃 Rust 的大部分优势。这意味着如果你想拥有 Rust 提供的所有优势,大多数依赖关系都需要用 Rust 重新实现。Go 也有同样的问题,但他们背后有谷歌的支持,可以推出大量的库。谁在为 Rust 做这些?
最后,我同意文章的观点: Rust 是伟大的!当它适合的时候。但在很多情况下,Rust 还不如老派 C 语言。
> 在我看来,Rust 还存在一个严重的 “不是在这里发明的 ”问题。在 C++ 或 Java 中重用 C 代码易如反掌。而在 Rust 中重用 C 代码则要困难得多……
这感觉像是对 Rust 的错误描述,我坚决不同意。Rust 语言和工具从一开始就是为与 C 代码互操作而设计的。如果你尝试过,这并不难做到。
> ……除非你选择不安全,放弃 Rust 的大部分优势。这意味着,如果你想拥有 Rust 提供的所有优势,大多数依赖关系都需要用 Rust 重新实现。
如果你使用不安全的 Rust,Rust 就毫无用处了”,这种说法已经被驳斥得体无完肤,但仍然有人这样重复。使用 Rust 语义来确保 C 语言等 FFI 的安全性是不可能的,因此使用 unsafe 是必要的。但是,使用不安全语言并不意味着该语言失去了大部分优势。事实上,几乎所有的 Rust 程序都使用了不安全代码,如果你愿意看看标准库中的代码的话。然而,这似乎从来都不是问题。
不安全代码的作用是封锁一小块代码区域,在那里必须手动断言某些不变性,而不是依赖借用检查器。其他代码仍然是安全的。即使出现安全违规,需要调试的代码也会大大减少。考虑到即使在最糟糕的情况下,不安全代码块通常也只占代码的不到 5%,因此与 C++ 等语言相比,这是一个很大的优势。但更进一步说,C 语言库通常有相应的 Rust 封装库,可以在 C FFI 调用上创建安全封装。除了可以手动验证小的不安全代码块外,这些封装库还有一个额外的优势,那就是可以被更广泛的社区使用和调试。crates.io 上有无数这样的封装器,指责这种语言患有 NIH 综合症是不公平的。
“如果你尝试过,这并不难”。
我尝试用 Rust 进行 OpenGL 渲染,结果发现这是一段漫长的旅程,我在不安全的 C 共享库周围经历了太多层 Rust 封装。我尤其痛恨的是,我不得不不断地使用铸造辅助函数,将预定义的 OpenGL 常量转换回相关的整数值。
如果 C 语言中只有一个 OpenGL API,而 Rust 中却有 100 多个用于封装各种 OpenGL 的板块,那么我认为指责 NIH 综合症是有道理的。
> 我尝试用 Rust 进行 OpenGL 渲染,结果发现这是一个漫长的过程,我在不安全的 C 共享库周围经历了太多层 Rust 封装。我尤其讨厌不断地使用铸造辅助函数,从预定义的 OpenGL 常量返回到相关的整数值。
从描述中,我看不出你使用的是 FFI 还是封装库。但看起来你是在抱怨这两种情况–这不可能同时发生。你提到的问题似乎与你的选择有关。我在其他代码中也遇到过类似的问题–通常是通过选择不同的库或方法来解决。我不认为这会影响 Rust 的 FFI 故事。
> 而且,如果 C 语言中只有 1 个 OpenGL API,而 Rust 中却有 100 多个用于封装各种 OpenGL 的板块,我认为指责 NIH 综合症是有道理的。
顾名思义,如果板条箱是封装器,他们就没有发明任何东西。他们只是使用了 1 个 OpenGL API,而这正是你指责他们没有做的事情。至于 100 多个 Rust crates,我认为这不是问题。封装开发者经常会尝试不同的 API 风格。只是crates.io让它们都非常明显。在实践中,只要有一个crates(或一组相关的crates)流行起来,就足以成为某个细分领域的事实标准。
> 重复使用 C 语言要困难得多
并非如此,而且你对自己在 OpenGL 方面的经验过于自信了。
有很多 Rust 库都是对 C 库的封装。用 C 编写的 libgit2 是最流行的 Rust 库,用于 git 绑定。
尽管如此,人们还是更喜欢纯 Rust 库,理由很充分–它让项目的构建,尤其是交叉编译变得非常容易。这也是 cargo audit 从 libgit2 转向 gitoxide 的原因。
谁赞助了这些库的开发?Rust 基金会为 gitoxide 的开发者提供了开发资助,最终目标是在 cargo 中取代 libgit2。
我很遗憾你在使用 OpenGL 时遇到了不愉快的经历,但我认为将你的经历推断到整个生态系统上并不准确。
> 简单的事情,比如在不可变数组中返回一个对槽的 const 引用,在 Rust 中的源代码量是 C 语言的 10 倍。
我对您的意思很感兴趣。这两种情况的语法不是一样的吗?
我个人对这个问题的看法是,Rust 把专家变回了初学者,一些开发人员因为这种恐惧而产生了憎恨的反应。对我来说,情况恰恰相反。我对自己所从事的行业感到厌倦,并打算转行做其他工作,比如管理。6 年前,Rust 的出现(对我来说)给了我一线生机。我喜欢在一件很难的事情上取得成功,这让我非常满足。我明白为什么有些人无法忍受这种语言。它的基因和生命周期注释看起来很丑,当一个项目大到开始落后于 Rust-analyser 时,我的喜悦也会变成愤怒。
> 我个人对这个问题的看法是,Rust 把专家变回了初学者,有些开发人员会因为这种恐惧而产生憎恨。
这些人可能是很差的开发人员。在软件开发这样一个不断进步的领域,任何没有继续学习的态度和心态的人都不适合这个领域。
也许对不喜欢用 Malbolge 编程的人这么说并不合理,也许 Rust 的某些方面更像 Malbolge 编程,而不像该领域在不断进步。
你本可以把时间花在学习一些真正有用的东西上,这样有助于找到有趣的工作。例如,GPU 编程或 SIMD 内核。
Rust 让我恼火的地方在于为了复杂而复杂,与其他替代方案相比,这种复杂带来的痛苦微乎其微。如果你需要async/await,现代.NET会更好,尽管有GC;如果你需要最高的CPU性能,C++会更好,尽管不安全。
“尽管有 GC “和 ”尽管不安全 “这两个 ”尽管 “实在是太重了。
这些都是大问题
我认为这并不那么简单。对于很多用例来说,使用 Rust 需要从头开始编写所有内容,因为唯一可用的库都是有主见的异步运行时,而且它们觉得有权在用户无法控制的时间调用全局分配器和各种系统调用,而这些组件又不能被替换掉,因为一些根本性的问题导致 Rust 库无法实现可组合(除非它们不遗余力地采用去年年底推出的功能?) 在我的经验中,由于 GC 而导致长时间停滞的用例相对较少,而由于页面故障或 open(2) 需要几秒钟才能返回而导致长时间停滞的用例则相对较多。
你在回复我时,好像评论是在真空中进行的
我是在回应
> 复杂性只是为了复杂而复杂、
> 以最小的代价换取最大的痛苦
> 如果需要异步/等待,使用现代 .NET 会更好,尽管会有 GC
> 如果你需要最高的 CPU 性能,尽管不安全,但使用 C++ 会更好。
对此,我的回答是:GC 是个大问题(我想你可以填上 “某些类别的问题”,但我认为这很明显),有些人需要避免它。而这些人通常会转向 C++,因为 C++ 中的 “不安全 ”是一个非常大的问题。
我不这么认为吗?我的回答是,如果你无法承受很长时间的延迟,比如至少在 60hz 下延迟几十帧,那么你的选择往往是用 C++ 处理不安全问题,用 .NET 处理 GC 问题,或者用 Rust 从头开始编写一切(包括许多你通常会使用库的东西),因为习以为常的 Rust 和相关生态系统无法解决你的尾部延迟问题。与 Unity 开发人员在经过多年开发后开始在 Switch 上测试时被迫采取的 “努力不制造垃圾 ”的做法相比,最后一种选择显然更有吸引力。
在.NET中,你也可以用 “C++”的方式来处理这个问题,因为你可以用结构体和泛型来编写抽象,而不是对象,还可以使用 malloc 和 stackalloc 等等。尽管有些人不这么认为,但你并没有被 GC 堆束缚住。
一般来说,60 赫兹不是问题,但随着高刷新率的普及,这个问题开始变得越来越重要。有一些更极端的情况,如 OSU 中的 1000hz 游戏循环,它几乎必须使用与使用基于 GC 语言的实时系统相同的技术:https://github.com/dotnet/runtime/issues/96213#issuecomment-…
这在很大程度上取决于代码的领域。仅仅说它们是大问题是完全错误的。有很大一部分应用程序根本不需要关心 GC。
>语法。太难看了。[……]我更喜欢不那么重符号的东西。
这也是我对 Rust 的不满之一。语法元素太重,很难在谷歌上搜索到。|` 字形的名称是什么?如果你知道字形在你的语言中的名称,但不一定知道在英语中的名称,那就特别具有挑战性。对于有多个公认名称的字形来说,挑战性加倍。
但最终,我意识到这并不是语言的一个非常重要的方面。
如果难看,那就难看吧。我还是会学的
此外,Bing Copilot 还为我解决了这个问题,因为它与谷歌搜索不同,它是语义的,而且不会忽略特殊字符。
我想维基百科有每个 ascii 字符的页面,如果你在维基百科中搜索 `|`,它会将你重定向到
https://en.wikipedia.org/wiki/Vertical_bar
其中列出了该符号的所有常用名称
这里还有一个表格,列出了所有符号,并提供了维基百科页面的直接链接
https://en.wikipedia.org/wiki/ASCII#Character_set
这可能有用
https://doc.rust-lang.org/reference/tokens.html#punctuation
但这似乎无法解释 “lifetimes ”或 turbo::<Fish>。
链接到 :: 的页面解释了 turbofish。
不过,你说得没错,它可以用一行来表示 lifetime 语法。
如果你知道在哪里查找,就能找到它。如果不知道,就找不到。
对于刚刚接触 Rust 的人来说,我不明白为什么你会期望他们在 x.collect::<Vec<_>>() 中的函数名和函数调用之间看到一个 “路径分隔符”。更有可能的发现途径是通过泛型链接(从“<”如此显眼的地方发现的),该链接显示了常量值上下文中的 ::<> 语法,而这实际上对理解并无多大帮助。
因此,所提供的链接并不是真的要 “教我语法的含义”。它的目的是为各种标记提供参考。它确实做到了这一点,因为 turbofish 并不是一个单独的东西,而是两个东西的组合。
如果这个页面的目标是 “向不懂 Rust 的人全面解释 Rust 语法”,我会同意你的观点。但它的目标是 “为那些想要了解语言细节的专家提供一份类似规范的文档”。
我刚刚克隆了 Starship 的 repo 并用 cargo –release 进行了编译,结果花了 6 分 30 秒(确实感觉很漫长)。作者说他 “仅构建 main.rs ”就用了 15 分钟,“lib.rs ”用了 10 分钟,“其他一切 ”用了未说明的时间。
我的 CPU 是 2012 年的。他到底是在用 TI-83 做这个?还是我忽略了 Gentoo 正在做的某些复杂工作,导致编译时间固有地增加了一个数量级?
> 作者说他 “仅构建 main.rs ”就花了 15 分钟,“lib.rs ”花了 10 分钟,“其他所有 ”花的时间不详。
这就奇怪了,cargo 显示的是编译下的当前板条箱,而不是文件,因为这不是 Rust 的编译单元,作者似乎在撒谎。
此人使用的是 gentoo,gentoo 会这样打印。
只是另一个数据点。我克隆并运行了 `cargo build –release` 。用时 2 分 2 秒(M1 Pro,2021 年)。
我只想说,创建一个发布版本真的很不寻常!你很少会这么做。大多数情况下,你会创建一个调试版本,在我的机器上,创建一个干净的版本耗时 29.56 秒,创建一个只修改了一行的增量版本耗时 1.45 秒。
我对作者运行在功率不足的硬件上表示同情,但这可能并不能反映一般 Rust 开发者的经验。
Rust 在 Gentoo 的打包系统中不像 C/C++ 那样好用,但没错,这似乎太长了。
不过话说回来,我很确定我是通过 `cargo install` 而不是 portage(Gentoo 的打包系统)安装 starship 的。
我是 Rust 和 Gentoo 爱好者 😀
5m 39s 在 2017 CPU 上运行: 英特尔(R) 奔腾(R) CPU G4560T @ 2.90GHz
人们抱怨 Rust… 正如 Bjarne Stroustrup 所说:“只有两种语言:人们抱怨的语言和没人使用的语言”。
这正是我想看到的信号。大家都喜欢的语言是不对的。在生产过程中,程序员必须遵循客户的要求,而不是相反。客户并不关心你的代码是否漂亮,他们只希望自己的东西能正常工作。随着时间的推移,丑陋的遗留代码会越来越多,你必须保留它,因为人们正在使用它,人们会抱怨得更多,他们会指责与之相关的一切:语言、客户、管理层、以前的开发人员等等…… 这其实是件好事,它意味着这种语言被用来做真正的工作,如果人们用它做的只是玩具项目或一些精确的、程序员驱动的程序,就不会有这些抱怨。
编程语言越烂越好。你决定 “我要用 Python,因为其他人都用它”,然后你浪费了一周的时间来调试,因为你团队里的某个混蛋把你传入关键函数的 dict 变异了,而你却没有注意到。
> 首先是语法。太丑了。在我眼里!
美丑由人,你不可能取悦所有人,也不应该超出你的怪异预算[1]。如此取悦 C++ 人群的决定(主要体现在后来与语法相关的决定中)并不是我的首选,但它是合理的,而且可能有更糟糕的决定。
> 第二、第三、第四、第五
我觉得很多对 async 的批评更多的是与 async 的使用方式有关,而不是 Rust 本身。也许有更多不使用 async 或甚至不依赖 tokio 的板块会更好,但归根结底,这是我们作为社区的责任。
> 第六,Rust 在静态分析方面走得不够远。Rust 本有机会走得更远,但团队搞砸了。
我没有资格评判这一点,但我很想知道其他人的意见。
> 第七,Rust 的编译时间是个笑话
没错,但这个问题已经在多个方面得到了积极解决。其中之一是 cranelift,我觉得它非常吸引人。
[1] https://steveklabnik.com/writing/the-language-strangeness-bu…
> 我觉得很多对 async 的批评更多的是与 async 的使用方式有关,而不是 Rust 本身。也许有更多不使用 async 或甚至不依赖 tokio 的板条箱会更好,但归根结底,这是我们整个社区的问题。
不,这是语言设计问题。在 Lua 或 Zig 语言中,编写一个会说 websockets 的库的简单方法是编写一个可以使用同步或异步 i/o 以及用户选择的读写原语的库。在 Rust 中,在经历了多年不必要的痛苦之后,我们终于有能力发布一个可以使用任何一种 i/o 的库….,除非用户想在同一个工作区中同时使用这两种方式,否则他们就完蛋了。你可能会喜欢这个故事:一个用户试图让 Rust 在一个库中实现 Lua 和 Zig 的默认功能: https://nullderef.com/blog/rust-async-sync/
此项工作正在进行中:https://blog.rust-lang.org/inside-rust/2022/07/27/keyword-ge…
这篇文章有点像咆哮,而且到处都是。Async 感染一切、难看的语法(谁在乎呢?
最让我印象深刻的是
> 换句话说,如果编译速度快,工具就很容易。
安德鲁-凯利(Andrew Kelley)在最近的一次演讲中也说过类似的话。编译器性能似乎是 Zig 目前的首要任务。
其理由似乎是:快速编译会带来更高的生产力,从而带来更多的错误修复。
这种说法看似粗暴,但却不无道理。
紧密的反馈回路(如 devops、REPL、TDD、轻流程等)可以帮助我们进行更多的迭代,并保持在最佳状态。
公平地说,像 Rust 这样复杂的类型检查器也有助于此。
当然,任何增加的摩擦和迟缓都会间接影响程序的整体正确性甚至性能。
快速编译是件好事,我认为增量编译之类的东西是必不可少的。
但归根结底,如果编译速度慢意味着要进行高级检查,我每次都会选择慢速编译。
说到 “区域”,我更喜欢花 1 小时编写 Scala 代码,同时修正编译器错误,然后运行它,它就能正常工作了 TM 与 Java 相比,Java 会让你浪费时间跟踪 NPE …
> 但归根结底,如果编译速度慢意味着可以进行高级检查的话,我每次都会选择编译速度慢一些。
我不认为这在 Rust 中是个大问题。与代码生成相比,类型检查相当快。
泛型、特质、宏等似乎是罪魁祸首。将复杂的语言特性转化为代码而非运行时安全导致了编译时间过慢。
> 丑陋的语法(谁在乎?)
我当然是个无名小卒,但我在乎。丑陋是主观的,很多人可能认为 “丑陋 ”是 “异类”,因为这不是他们所习惯的。较少的语法杂乱无章有利于认知负荷,而更多的通用语法则有利于采用。
但我相信这个问题会随着实践而逐渐消失。
> 语法难看(谁在乎?)
代码就是用来阅读的,所以很多人都会在意。
当然,语法并不是决定一门语言实用性和吸引力的唯一因素,但阅读代码仍然是我们使用代码的两件主要事情之一,因此正确的语法相当重要。
是的,但可读性和美观是两种截然不同的品质。
Rust 是披着羊皮的狼。它看起来像 C 语言语法,但在很大程度上又像函数式语言那样基于表达式。它也有很多语法。
正因为如此,大多数人只需花很少的力气就能读懂它。但代价就是丑陋。
> 理由似乎是:快速编译带来更高的生产率,从而带来更多的 bug 修复。
当然,如果再加上 LLVM,即使在 Zig 中也会出现编译时间过慢的问题(参见 Zig 申请与 LLVM 离婚 https://github.com/ziglang/zig/issues/16270)。当然,LLVM 并不是唯一的罪魁祸首。Rust 也有自己的问题,这些问题源于单态化和宏,类型检查似乎并不是罪魁祸首。不过,这也取决于项目。
编译速度快与优化效率高似乎是一个权衡的问题。
这种争论总是被分割成非常彻底的防雷战壕,简直是疯了。
反 Rust 的人认为这种语言的模板比 JavaScript 还多,是最复杂的语言,却没有任何好处,但事实并非如此。与 C/C++ 相比,Rust 是一门伟大的语言,它的许多特性都是巨大的优势。
Rust的布道者们把它当成了 “第二次来临”,认为如果你不让这种语言对你强制执行它的范式,你就是在杀死婴儿。锈语言提供的强制安全是 2024 年的一项伟大功能,但它并不是万能的–有很多地方并不需要它,反而会给程序员带来更多工作。这种说法通常也是 “所有东西都应该使用 Rust”,但当你指出一个使用 Rust 的用例中 Rust 的优势并不重要时,他们就会说 “重写只需要在 <x 关键直接网络服务> 中进行”–这就意味着所有东西都不需要使用 Rust,不是吗?
就像大多数事情一样,正确的道路就在中间。我个人不喜欢 Rust 的语法、泛型、不面向对象等,但这只是我的观点,与其他人相比并无优劣之分。归根结底,只要你了解每种语言的优点和缺点,就使用你最喜欢、最得心应手的语言吧。
– 署名:Kotlin 布道者
我咬一口: 人们觉得 Rust 语法的哪些具体部分很难看?
我一直听到很多不写 rust 的人这么说,但在我看来,这种语言与 C 语言相差无几。它肯定不是 haskell。
从我询问过的人来看,抱怨主要分为两类:
* 熟悉和美观。Rust 使用的圆括号()较少,而 <> 和 {} 较多。这让它看起来很陌生,当人们无法立即识别他们熟悉的语言结构时,就会说它不可读。
* 将学习生命周期和泛型等 Rust 概念的难度错误归因于语法。人们说他们想要更少的语法,其实是指他们不想明确指定所有权和特质边界之类的东西。
我是作为一个 Rust 的爱好者这么说的,但我认为大多数人这么说的意思是,有很多事情要做,尤其是在函数定义语法中。当你开始添加生命周期、带边界的泛型、async、&muts、where 子句……它确实变得难以阅读。如果不把语法变得更加冗长,或者大力简化类型系统,使函数参数推理变得可行,我真的看不到有什么办法可以解决这个问题。
可能还有冒号(::)。这些都会增加很多噪音。
我怀疑冒号是个错误。Java 模块和类都使用句号,这似乎不是问题。
生命周期真的很难看。你通常不需要明确地编写它们,但这并不是一个真正的借口。
> 冒号
我更喜欢 “四点”。:)
https://web.archive.org/web/20230713231438/http://bash.org/?…
Paamayim Nekudotayim 任何人 (https://en.wikipedia.org/wiki/Scope_resolution_operator#PHP) ?
更有趣的后续问题是:”如何用不同的语法表达相同的语义?关于 Rust 丑陋语法的讨论往往缺乏这一关键要素。
> 如何用不同的语法表达相同的语义?
这不是问题所在,问题是”……并保持类似 C++ 的语法?
是这样吗?我不知道让 Rust 的语法保持 C++ 风格是否是这个项目的既定目标,但可以肯定的是,我们可以在我的问题中加上这个注意事项。
我认为最大的问题是,人们会说:“终身注解既丑陋又嘈杂”,而当被追问这个问题时,他们最终会承认他们只是想要 GC(这与语法无关,而是语义问题)。
这是一个目标,尽管优先级低于其他目标。你可以从 Rust 大体上采用 “大括号和分号 ”的方式看到这一点,但有时它也有很好的理由偏离这一点,比如 ML 风格的 let 语法。
> 我不知道保持 Rust 类似 C++ 的语法是否是该项目的既定目标。
他们不会改变语法,反正整个讨论都是假设性的。我想表达的是,大多数人都想要 C++ 风格的语法(泛型的),即使他们抱怨 Rust 的语法。
我认为,代码的具体内容并不重要,重要的是(有时是晦涩难懂的)符号的组合,这些符号组成了密集的符号块,你必须仔细抠开才能理解代码在做什么。如果代码使用的是单词而不是符号,就更容易推理,而 Rust 似乎极力避免使用真正的单词(我不认为 “fn ”是一个单词,“pub ”也是一个单词,但对我来说就是下班后去喝酒的地方)。好吧,我知道在 C 语言家族中这样做的传统由来已久,但 Rust 将其推向了新的高度,它有更多的概念(及其附带的符号)需要你记在脑子里。
我讨厌 Haskell 等语言中那些难以发音的符号,但 C 风格的缩写并不会让我感到困扰。缩写至少可以帮助你记住单词的意思,对我来说这似乎就足够了。
>我经常听到一些不写 Rust 的人这么说。
如果你想提高采用率,你就应该听听这些人的意见。
特质对象参数的生命周期界限可能会令人困惑。
我每次都要查一下如何正确地书写 where 部分。
这是我的试金石:大声朗读源文件。如果需要的话,可以通过电话向一个看不到你屏幕的人大声朗读。你是否对所有内容都有一个明显、易懂、简单的发音,而不只是一个一个地读 ASCII 字符?
现在假装对方是一个聪明而有经验的程序员,但从未听说过 Rust。在完全不了解 Rust 语法的情况下,他们会如何写下你刚才告诉他们的内容呢?这就是不丑版的 Rust。
Vs
当然,将示例限制在 Rust 语法的子集上会产生更可读的答案。你没有生命周期和借用,泛型也非常有限。
人语法 “Rust 语法的信息密度都保持线性大小,但有明显不同的 N。
Vs
If you want a real life example:
我并不是说复杂的东西会变得不复杂。
然而,Rust 的人类语法都不包括::或”这样的东西。第一近似值[1]显示,这些部分会让人觉得 “丑陋”,而它们并不存在于人类语法中。这就是人们所说的 “重符号 ”或 “基于标点符号 ”的语法的含义–一般情况下,人们不会把这些东西读出来。在这里,你可以对 “美 ”进行论证。
[1]: 只是大致如此,因为你选择的人类语法仍然编码了 Rust 的一些语法决定,比如预先声明泛型生命周期和类型,以及使用 “where ”而不是内联子句。这些语法部分也可以根据 “不丑陋 ”的主观价值进行调整。
鱼尾语法、生命周期语法和闭包语法都相当丑陋。只是在实践中结合在一起时特别丑陋,而不是在孤立的例子中单独丑陋。
不管怎么说,“涡轮鱼 ”的出现是由于决定使用 <> 来表示泛型(为了让 C++ 和 Java 开发人员熟悉),以及希望使语法不含混(从侧面解决了 C++ 的问题,即必须延迟确定某些东西是类型路径还是比较,将解析和名称解析混合在一起)。
> 人们觉得 Rust 的哪些具体语法部分很难看? ……在我看来,这门语言与 C 语言相差无几。它肯定不是 haskell。
把 “和 C++ 差不了多少 ”改成 “和 C++ 差不了多少”(相比之下,C++ 真的很美),这就是你的答案。
帖子似乎没有提到社区的某些部分,这基本上是我对 Rust 的主要意见。
你甚至不能提及引导编译器是一个过于复杂的过程,因为你会被遗忘(“你为什么需要那个”)。幸好已经有了一些,尽管还不完整,因为(我猜测)Rust 大多被定义为 “无论 rustc 输出什么”,这无助于减少检查你的实现是否正确的痛苦。不过,我很欣赏为编写关于该语言的非正式非规范文档所做的努力,有总比没有强。
此外,我也不能说替代编译器会更好(例如,让引导更容易,见前一点),因为你也会被遗忘(“不要分裂生态系统”、“Rust 理事会/团队/等从现在到永远都会由值得信赖的人组成”)。
比如,是的,但冷静一下,试着考虑一下其他用例,而不是因为你认为不需要,就把这些话题解读为对语言的攻击。少数人大声拒绝任何健康讨论的尝试,这对生态系统来说可不是什么好现象。
这种情况也会发生在其他语言的社区中,但不知何故,我似乎发现在 Rust 中发生得更为频繁。
(附注:稍微改变一下话题,我引用坎宁安定律说,没有一种离线阅读 Rust 文档的简单方法不需要某种网络浏览器或解析 HTML 输出。类似于 “go doc ”或 Python 的 “help() ”函数)。
> 这篇帖子似乎没有提到社区的某些部分,这基本上是我对 Rust 的主要问题。
我也是,但我的帖子暗示了这一点,而且它已经太啰嗦了。
在 Rust 内部,它是一种更小、更简单、更安全的语言,正在努力摆脱困境
没有 GC?没有。当然欢迎你尝试,但除非你是类型理论博士,否则我怀疑你的机会。
作者在此。
姚已经完全设计好了。虽然我还没有更新它的示例文件 [1] 以包含所有内容,但它最多只会扩大两三倍。
这包括泛型、Zig comptime 的等价物、类似 traits 但功能更强大的东西、表达式问题的解决方案等。
解决方案就是结构化并发。
我没有在 Yao 上投入更多精力的唯一原因是,我全力以赴地工作了三年(因为我不断重构以消除技术债务),我已经精疲力竭了。
但添加一个新功能最多只需两个小时;我只需添加一个新的客户定义关键字。
[1]: https://git.yzena.com/Yzena/Yc/src/branch/master/src/yao/exa…
Rust 中还有一种更小、更简单、更安全的语言,叫做 “Rust without async”,它足以满足 99% 不需要绝对最低延迟 async 的用例。
没有人强迫任何人使用 async。它完全是语言的可选部分。
但如果认为不使用它就能更容易地找到解决方案,那就太天真了。
我不太确定,但 Austral-lang (https://austral-lang.org/) 非常接近我想要的更小、更简单、更安全的 Rust。
它的语法受到了 Ada 的启发,而且据我所知,它的设计者并没有类型理论方面的博士学位,因此它也符合其他评论者提出的要求。
可读性是一个很好的优点。
我不确定是否更小或更简单,但在我看来,Ada 绝对更具有可读性。
这个,我不明白我们为什么要在这些语言中加入这么多语法,我们怎么会觉得有这么多语法是有益的,这真是令人费解。我承认我偏向于符号表达,但我们肯定可以想出一些中庸之道。
有两点:
– 让 C++ 专家易于使用
– 我们的想法是,语言可以毫不费力地进行grepp。
来自 C++,而我很希望 Rust 不要捡 C++ 的东西来让它看起来像 C++…
从一开始就背上了传统的包袱。这正是我所感叹的,它客观上是一种语法汤。
这是我目前挑选语言的思维模式,只考虑语言本身:
– 后端: Gleam 或其他 BEAM
– 网络前端 Gleam
– 移动应用程序: Dart + Flutter
– 专业移动应用程序:Swift 和 Kotlin
– 快如闪电 Zig
– 最快速、最安全 Rust
– 快速性能和迭代: 围棋
欢迎补充和更正。
我唯一的问题是我不喜欢 Go 的语法,不知道有什么好的替代方案。我听说过 OCaml 的好话,但还没去了解过。
OCaml 几乎可以替代所有这些。我想现在还没有 BEAM 编译器后台。
我对 BEAM 不是很有经验,但它的功能是否可以通过不同堆栈之上的框架来实现?我知道 Akka 很流行。
BEAM 的 OCaml 后端曾被称为 Caramel,但在其作者继续开发自己的构建系统 warp 之后就被放弃了。
构建系统是为我们最优秀和最聪明的人准备的。
和你的亲朋好友谈谈吧。
演员模型只是 BEAM 的一小部分。即便如此,它也能保证调度器的产量。
这实际上是不可能加装到现有语言中的,尤其是在存在循环的情况下。
GC 使 OCaml 无法取代其中的一大部分。
>>BEAM编译器后端还没有出现
我认为有一个接近的东西 – https://caramel.run/manual/
该项目已被放弃 : https://github.com/leostera/caramel/discussions/102
我现在超级喜欢 elixir,再也不想去别的地方了。gleam 真的有那么好吗?有什么优势?我可以使用 liveview 吗?
据我所知,Gleam 是 BEAM 上唯一的强类型语言,这对我来说很重要,因为我不喜欢动态类型语言。当然,Elixir 现在也有可选类型了。
还有
Purerl – PureScript 的 Erlang 后端,一些人正在生产中使用它 – https://github.com/purerl/purerl
Caramel – Beam 的 Ocaml,似乎已死 – https://github.com/leostera/caramel
还有更多可能已经死亡的项目,请访问 https://github.com/llaisdy/beam_languages
这是唯一的区别吗?
它还可以编译为 JavaScript(而不是 BEAM),因此您可以在后台和前台工作中使用相同的语言。
– 后端: Kotlin(在 JVM 上)
– 网络前端: TypeScript(也许将来会用 Gleam!?)
– 如果我想要二进制文件,就需要快速的性能和迭代: Kotlin(本地编译)
– 速度极快,适合 WASM: Rust
– 我关注的语言: Gleam、Zig、Odin
– 我永远不会接触的语言 C、C++
– 我认为古板的语言:OCamel、Lisp、Haskel OCamel、Lisp、Haskel
– 我过去用过但还不错的语言 Dart
– 我过去用过但还不错的语言:Java(如果它有 nullability,那就更好了): Java(如果它有无效性,那就更好了)
我喜欢 Kotlin,但不想使用 IntelliJ,他们显然有强烈的经济动机反对支持其他 IDE。这方面有什么变化吗?
我很欣赏他们在native/wasm方面所做的工作,我认为如果他们能因此获得经济奖励/赞助,那是再好不过的了。只是不幸的是,他们必须依赖于集成开发环境。
Nim 符合 Go 的许多用例。不过它更小众一些。
我看到了很多对 async Rust 的批评。
在 C# 中,大多数 API 都同时公开了相同方法的同步和异步版本。为什么这种情况在 Rust 中不常见呢?
当然,一些第三方 C# 库可能会在内部进行一些难看的同步到异步的转换,但大多数库并没有这样做,而是使用 .NET 标准库中相同 API 的同步/异步版本。
Rust 的 async 在某些地方更难使用,因为 Rust 着迷于避免分配和动态派发。
C# 和 JS 等语言会为每个 Promise 和每个监听器分配一个新对象。Rust 则向后弯曲以避免这种情况,而是使用内联的强类型状态机。这在你意想不到的地方造成了限制,例如递归调用需要特殊处理,抽象接口不完全支持 async。
至于两者都支持,这只是不方便做到而已,因为许多微小的语法和语义差异意味着很难自动化,维护起来也很麻烦。
在 C# 中,并非每个 await 和 new 任务都会产生分配: ValueTask 只有在异步产生时才会分配一个状态机框,普通任务对象会被池化为常用值,状态机框本身也会被池化为频繁调用的方法(选择加入),例如,socket.SendAsync 不会分配状态机框。
随着 “运行时处理任务”(Runtime Handled Tasks)实现的出现,这种情况将进一步改变,它将用运行时提供的暂停机制取代 Roslyn 生成的显式状态机代码,这种暂停机制只会在真正的屈服点屈服/暂停执行,并进一步改进捕获的状态的存储方式。
也许对某些人来说,这听起来很平庸,但如果 C 语言有一个 “货物 ”系统,并且没有隐式导入,那我就会觉得更愉快了。
这就是 C 语言的问题所在。这种语言并不是为编写库而构建的。当涉及到 C 语言中的数据结构时,每个人都在重新发明轮子,这是有原因的。
最接近的是 GLib,但使用起来绝对不好玩。
您可以使用 Zig 构建系统作为您的 C 构建系统。参见 https://zig.news/kristoff/make-zig-your-c-c-build-system-28g…
货物只是故事的一半。另一半是 Rust 本身。它是编写抽象的绝佳语言。
这就是我喜欢 zig 的原因,它至少有最基本的 build.zig 构建脚本。您还可以使用 build.zig.zon 获取 build.zig 依赖项。
“我喜欢依赖关系少的小程序。我会自己编写 I/O,以避免依赖。而 tokio 对依赖关系的作用就像 god 对象对面向对象编程的作用一样”。
这就是阻碍我使用 Rust 的原因。它与库的包管理器集成得如此紧密,以至于试图避开 crates.io 和依赖关系就像逆水行舟。
我找到了一些示例 https://rigbuild.dev/yao-tutorial/
您还可以查看示例文件。
[1]: https://git.yzena.com/Yzena/Yc/src/branch/master/src/yao/exa…
@gavinhoward 顺便说一下,文章中有几个链接坏了:
* C UB 的 “不完整列表 ”目前指向 Rust 规范的愿景
* 标有 “不合理的编程语言 ”的链接目前指向 Gentoo 维基百科页面
* Starship “链接指向关于维柯丁的 House Wiki 页面
咳!这就是我在深夜醉生梦死的情况下快速撰写文章的后果。
咆哮也是副作用之一。
已修正,谢谢!
我基本同意。编译时间是业界最糟糕的,异步也是一团糟。
编译时间是这里提到的唯一真正的问题。
我仍然认为它比 C++ 好,但对于大型项目来说,编译时间和板条箱分割等问题确实令人头疼。
不是作者: 不完整列表 “超链接的 URL 不正确。
啊!谢谢,已修复。
这位作者对 Rust 的很多抱怨让我想起了历史上对 C++ 的类似抱怨,只不过被放大了。
劫持和超越新项目?C++ 和 Rust 都是这么做的。C++ 的编译时间就是个笑话,Rust 更是如此。复杂的语义和语法?两者都有。Rust 试图成为所有人的一切?C++ 一直被批评为委员会设计。
然而 C++ 依然存在,并被广泛使用。一旦硝烟散去,人们就会痛恨 C++ 及其带来的一切。在一切都尘埃落定之后,C++ 因为没有这些令人头疼的问题而变得更容易被接受和使用。C++ 依然存在,当然也没有形成 C++ 的单一文化。我希望并期待 Rust 也能上演同样的故事。
你的意思是说,一旦 Rust 的硝烟散去,人们就会认为 C++ 更能忍受、更可用?)
复杂语义的例子有哪些?我能想到的东西(生命周期、泛型、特质)在 C 语言中也存在和建模,只是语言和编译器的支持较少(或没有)。
采用 Rust 的项目让贡献变得更无趣、更痛苦
我试着修复 Zed 的错误,每次更改数值都要等待 30 秒,非常痛苦。
至少,编译总是简单明了的。而使用 C 语言时,编译很有可能会因为某些原因而失败(例如操作系统缺少某些头文件)。
是的,我认为 Rust 会让贡献变得更容易,因为我一般不需要去寻找设置说明,这些说明可能存在也可能不存在,可能对我的系统有效也可能无效。
而且编译器至少会在编译失败时告诉我问题出在哪里(并给出建议,不过在非琐碎的情况下,这一点也很难做到)。
编译器唯一帮不上忙的地方是,当我试图找出一个 future 为什么是 !Send tho 时。
我觉得你可以在这里对 “有趣 ”多加修饰。我和你的经历并不一样–我随时都可以做一个 Rust 项目–但我很想知道你觉得缺少了什么。
(除了众所周知的编译时间)。
你没有理由不在开发时使用 Rust jit,然后在生产时编译代码(或者在生产时也使用 jit)。
没有理由?你确定吗?
我想你的意思是理论上可以有一个解释型 Rust,但我不认为有人做过 Rust 解释器的原型。
最接近的可能是 Rust-analyzer(官方语言服务器),它会维护内部状态并对你所做的更改做出反应,但它不会创建可执行的工件。
另一个可能是 Cranelift Backend(https://github.com/rust-lang/rustc_codegen_cranelift),它可以快速生成调试构建。
我指的不是技术上的原因。人们可能会说大部分编译时间都花在了类型/借用检查器上,但这并不重要。您可以对可能不正确的代码进行 JIT,并将静态检查推迟到以后进行(或并行运行)。
cranelift 后端本身也有 JIT 模式。
老实说,这可能比未经优化的 aot 计算还要快
有些开发人员需要/习惯于快速的编辑-编译-运行循环。JIT 甚至不需要优化,只要能改善开发人员的体验就足够了。
你在等什么?你不应该每次修改都运行货物编译。货物检查也不应该花那么长时间。
这是个有点奇怪的现象,但我注意到,我正在专业开发的 Rust 软件在推向生产之前从未真正运行过。我们会进行一些单元测试并运行这些测试,如果没有问题,就会推送到生产环境。老实说,我觉得这有点疯狂,但它确实有效……
做你想看到的改变。
出于种种原因,我拒绝使用 Rust。
我希望这股 Rust 风潮尽快退去