在架构层面上,Rust不适合哪些场景?


本篇文章摘录自reddit,标题是:What is Rust NOT suitable for, architecturally? 链接:https://www.reddit.com/r/rust/ ... ally/

从语言架构的角度(而非生态系统的角度)来看,Rust自身的哪些局限束缚了它的实际应用?或者说它的哪些特性会严重降低生产力水平?Rust不适合于哪些场景,又有哪些语言的设计范式特别适合这些场景?

我听说过很多不适合用Rust的案例,最典型的就是带垃圾回收机制的JIT编译。这是真的吗?

Prokopyl:

我个人心态比较开放,希望“根据不同需求找到正确的工具”,所以我想从另一个角度聊聊这个问题。

在我看来,与其他一切编程语言(不只是C/C++等系统编程语言,也包括所有其他语言)相比,Rust最大的设计特性在于:
  • 能够对系统细节实施低级控制。包括引用、内存分配与布局、线程/异步等(在嵌入式领域体现得尤为明显)。
  • 极高的性能水平(延迟也低)。其延迟基本低于1毫秒,而且每秒能够完成数十甚至数百万次处理(无论大家怎么具体定义「处理」,Rust都能实现)。另外,Rust还会严格控制内存使用方式。
  • 非常注重正确性,特别是在错误管理方面。Rust的座右铭是“failure isn’t an Option”。


所以这些优势在你的使用场景中越不重要,Rust的妨碍效果就体现得越明显(导致生产力下降),当然也就越不适合你的任务:
  • 如果你不关心数据在内存中的布局,数据结构(例如OOP中的对象/类)单纯作为代码组织架构,那么Rust语言和标准库中的相当一部分内容就会显得很奇怪、很愚蠢甚至令人讨厌。例如&引用、Box、Cell、RefCell、Rc,还有String、Send与Sync等多种flavors的区分等。对于真正关心精准内存建模的系统编程语言来说,这些功能可谓必不可少;但如果你只关心“这些数据确实存在于内存当中”,那它们就纯属多余了。
  • Rust拥有一流的错误管理设计,但注意只在必要时使用,否则绝对折磨死人。比如在发生故障时,相较于粗暴地向用户显示“抱歉,出错了!”,能捕捉并显示异常细节显然效果更好。

  • Rust的性能优势基本取决于实际情况,但我们首先需要明确以下几点:
    • 你的工作是否属于一套拥有严格限制要求的系统?例如“我想在数据中心内开发一款每秒能生成TB级数据的程序,Rust能不能搞定?”当然可以搞定,这也是它强大性能的体现。
    • 让程序运行得更快,对于用户有没有实际意义?例如,如果可以使用fd或者ripgrep在几秒之内翻阅根目录,我就不需要使用索引搜索。如果能高效完成实时音频处理,我们就能在CPU资源被耗尽之前再添加一些有趣的元素。如果图形处理更快,我们就能在流畅的UI动画中添加更多推送内容,由此建立起更直观的视觉反馈。
    • 如果以上情况均不适用,那你的用户有没有“可以接受的性能下限?”例如,大多数情况下我们要求按钮在20毫秒之内对点击操作做出响应;这时候即使把响应时间再缩小一百倍,体验上也不会有太大区别。所以在本质上,性能的收益递减是从哪里开始的?
    • Rust的性能设计可以理解成“投入更多工程资源,让程序运行得极快”,但这样的工程成本具体有多高?有没有其他成本较低的选择也能实现高性能(例如购买配置更高的计算机,或者购买/租用更多计算设备),这些方案到底能省下多少工程资源?
    • 如果你在这些问题上基本都给出了否定的答案,即“不需要那么高的原始性能(或者负担不起工程成本)”,那么Rust确实不太适合你。之前你提到的带垃圾回收机制的JIT编译就属于这类情况。


另外,我还想用亲身经历解释一下我们自己的Web应用为什么没有在后端上选择Rust(我们做过很多尝试,最终选择了C#加F#):
  • 我们不需要对任何事物进行低级控制。从根本上讲,我们的后端只需要接收一些HTTP请求、从数据库中获取/更新一些数据、执行一些业务逻辑,之后把这些内容序列化为JSON形式(偶尔再加上一点加密操作)。Rust的很多功能特性在Web场景下实在有些大材小用,市面上为这些需求提供有丰富的高级、久经考验的实现与API选项。另外,我们也不想在这么简单的用例中处理内存管理之类的麻烦事。
  • 对我们来说,错误/可靠性部门的工作压力不大。就算真的发生故障(例如无法访问数据库),后端要做的也只是向我们发送崩溃报告并向用户返回500错误。
  • 在性能方面,我们目前的开发阶段下最多可能只有几百位并发用户(最极端的情况下也只会转化为每秒几十个HTTP请求),仅此而已。如果服务器性能捉急,基础设施会随用户规模同步扩展,我们的计费政策也已经考虑到了这一点(我们的所有用户均为付费用户)。所以只要请求的处理时长没有特别夸张(不超过50毫秒,后台请求不超过200毫秒),就不需要额外关注性能问题。


结合上面的描述,相信大家会觉得Rust在多数场景下并没什么实际优势。我自己目前也是这样的观点。

在我看来,Rust的新颖之处在于大大提高了系统编程的可靠性与范围。但从本质上讲,Rust仍然是一种低级的系统编程语言(虽然它能构建起多种多样的高级抽象)。所以不涉及系统编程的程序员几乎体会不到Rust的好。那么,Rust到底好在哪?

重点来了,我觉得Rust确实拥有着一个大多数其他语言都不具备的优势:强大的互操作性。

帖主之所以提出问题,在很大程度上都是在讨论Rust适不适合替代原有项目中的某种其他语言。答案当然是不一定,要分情况而论。但这个问题之所以成立,就是因为Rust在其他语言的运行时中已经能够良好集成(而且会越来越好),完全可以跟最适合项目的语言稳定共存。

所以我之前提出的观点不仅需要结合项目整体情况,还要考虑到项目中的各个组成部分。面向Web的典型架构(三层、微服务等)已经大大降低了开发门槛,我们还可以通过Rust实现库为项目引入更多精细控制。

例如,我自己日常工作中接触的大部分前端都属于标准的Vue应用程序,但我们也会在其中利用Rust(构建和打包成WASM)处理浏览器无法支持的加密原语。所以说Rust特别适合在应用程序的特定部分发挥作用。我们有时候就是得利用多种工具共同完成同一项既定任务。

再说点跟问题无关的题外话,但我觉得这也非常重要:与其他大部分语言相比,我认为Rust非常有趣。对于那些限制要求不多(财务或技术)的项目,选个好玩但不太适用的工具也没问题。毕竟完全没乐趣的编程就成纯折磨了嘛。

MaybeAStonedGuy:


Rust拥有一流的错误管理设计,但注意只在必要时使用,否则绝对折磨死人。比如在发生故障时,相较于粗暴地向用户显示“抱歉,出错了!”,能捕捉并显示异常细节显然效果更好。
Result<_, Box<dyn std::error::Error>>特别善于处理那些几乎没有类型的异常,这时候它仍然能够凭借std::error::Error: Debug + Display正常输出错误信息。

这也是Rust最得我心的地方。我可以先构建起程序的绝大部分逻辑并完全运行,再从内而外地根据喜好细化错误类型。

我基本同意你的观点。我认为Rust的主要痛点是缺少其他语言中已经存在的实现库;例如具有完备功能的XPath、XSLT以及对XML Schema的支持等等。如果Rust在库支持水平上能向其他更成熟的语言看齐,我肯定愿意立马“弃暗投明”。

simonask_:

Rust不适合做快速原型设计。没错,不是做不了,只是不适合。Rust语言强迫开发者进行实际工程,这种特性在实际交付产品时非常有用,但在快速构建概念证明时却会带来很多无用功。

而不同于Rust,经典的面向对象编程确实在某些领域能大放异彩,具有丰富运行时反射的其他动态语言在这方面也拥有令人惊叹的表现。

还有一些算法能充分发挥垃圾收集机制的作用,例如实现无等待链表。而这一切在Rust中都更难实现。

0 个评论

要回复文章请先登录注册