为何Symless选择Rust,而不是Go、C++或Node.js?


我们决定为 Synergy 3 后台服务(目前用 Node.js 编写)使用Rust(不是 Go 或 C++),因为我们相信它会给我们的客户带来更好的体验。没有考虑 Java。
我们选择 Rust 并不是因为它是Stack Overflow 连续四年最受欢迎的语言,尽管大多数跳到 Rust 的人已经爱上了并留下了。我们选择 Rust 主要是因为它的内存安全优势。顺便说一句,Rust 比其他语言更环保,而且 Rust 社区最欢迎跨性别者(考虑我们的招聘策略)。
 
Synergy 3 由三个部分组成:

  • C++ 核心(执行实际的鼠标和键盘共享),
  • Electron GUI(用于配置,用 JavaScript 编写,带有 React),
  • 以及Node.js 服务(一个临时原型,用 TypeScript 编写)。

该服务提供自动发现(通过 mDNS),使所有计算机之间的配置保持同步,并将在未来提供其他功能,例如所有计算机的一键式自动更新。
 
在决定需要用比 Node.js 更好的性能来编写服务后,我们最终决定将 Rust 和 Go 作为可能的选项(根据我们从 Synergy 2 中获得的经验,早期不包括 C++)。
团队花了一些时间在 Rust 和 Go 中开发了一些小型演示项目,以了解这些语言。Rust 和 Go 是非常不同的语言,因此很难比较。尽管如此,我们还是与开发团队开会,并提出了一个电子表格矩阵。

以下是我们在决策表中的含义的一些定义。很多评分都是非常主观的、特定于团队的和特定于项目的。因此,这些数字可能对其他项目或与其他团队的决策没有用处,但它应该让您了解我们是如何为 Synergy 做出决策的。
  • 稳定性:Synergy 3服务的稳定性如何(即内存安全,等等)。
  • 开发时间:在我们有的时间内,我们能够做出多少开发过程。
  • 培训/采用:该语言对各种开发人员来说有多容易掌握。
  • 招聘--成本:企业要花多少钱(招聘广告、管理、面试等)。
    招聘--受欢迎程度:有多少开发者真正想使用这种语言。
    招聘--可用性:有多少人在寻找该语言的角色。
  • 库:是否有我们需要的库,它们是什么样子的?
  • 社区:社区有多强大,多平易近人,多有帮助?
  • 语言特点:语言功能是否能很好地满足我们的需要?

 
内存安全是首要决定因素
我们发现,内存错误是我们最大的担忧,因为Synergy 3服务是一个长期运行的过程。我们还考虑到,在2019年,微软揭露了他们70%的bug都与内存安全有关。

对于内存安全,我们有几个选择。用Node.js和Go编写的代码可以通过足够的工作来实现内存安全,但它们都是基于GC的,这不可避免地导致工程师容易忽略内存安全问题,不自觉地依赖语言运行时来清理它们。另一方面,C++没有GC,所以你必须对内存管理进行更严格的管理,但语言本身并不能保护你。你不能把内存泄漏的原因归咎于C++,只能怪你自己。例如,打破封装或将C语言的指针运算与C++混合在一起,就会出现内存错误......但这很容易做到,特别是对于经验不足的开发者。unique_ptr怎么样?当然可以,但你不一定要用它。
 
另一方面,Rust根本不允许你对内存不安全(当然,除非你故意泄露内存)。这是一把双刃剑,因为有两个原因。

它使学习语言变得更加困难;借用和所有权的概念需要花时间来学习。例如,如果你用同一个变量调用一个函数两次,Rust会很不留情面,因为你会得到一个编译器错误。C++、Go和Node.js会让你做这样的事情,而没有任何直接或明显的后果。
在Rust中,你不得不考虑内存分配和内存管理。是的,也许在某种程度上,C++也会让你思考这些事情,但不是通过直接的编译器错误......相反,在C++中,你经常在运行时学习这些。微软安全工程师马特-米勒(Matt Miller)因谈到过去十年中微软的大部分补丁都是对内存安全漏洞的修复而闻名,这可能是Hyper-V团队考虑使用Rust的一个因素。
 
为什么不坚持使用Node.js?
Synergy 2服务(在那里犯了错误)纯粹是用C++编写的,所以最初我们在选择1)用C++重写Synergy 3 Node.js服务,和选择2)坚持用Node.js来做服务。

Node.js在突破和快速编写代码方面非常出色,所以它在原型阶段非常好。如果用Rust、Go或C++,那就有点麻烦了,因为它变化太大,而且我们也不完全确定我们编写的代码是否是适合我们客户的解决方案。但是,我们对现在的情况相当肯定,所以整个开发团队觉得是时候把代码移植到使用内存和CPU较少的地方了。

Synergy 3服务的后台进程将在用户的桌面上连续运行数月,在后台安静地工作。所以,内存错误导致了可怕的用户体验(如果用户不是很有技术含量,他们不知道为什么他们的电脑变成了一个滞后的烂摊子)。实际上,我们在Synergy 2的C++服务中看到了类似的内存安全问题,所以这是一个我们经历过的真实问题。

对于我们的产品,使用Rust而不是Node.js会带来更好的用户体验。 
 
但你不能真的将Go与Rust进行比较
我与我最信任的CTO联系人中的大约20人进行了交谈。为了保持客观,每次谈话都以这样的方式开始。"知道Rust或Go吗?"

以下是其中一次谈话。他的回答。

"[Go]说到底只是另一种类似C的语言。 Rust则完全是另一种野兽。根据我所接触的其他人的传闻,大多数人都喜欢它的想法,但发现它非常难以操作。 如果你问我,我想它会因为这个原因而昙花一现,但谁也说不准。"
我问他,他是否认为Rust可能会昙花一现,因为它比Go更难操作。
他回答说
"嗯,也许只是一般的难,而不是明确地与Go相比。 在我看来,它们的目标市场是完全不同的。我认为Rust应该是C++的现代替代品--即非常接近裸机的东西;而Go我认为是针对更高层次的应用。 因此,我认为这实际上是Rust与C++的对比,而不是Rust与Go的对比"。
 
Discord Gophers服务器上的Go开发者倾向于同意这个观点;Rust并不具有真正的可比性,因为它的目的完全不同。

有趣的是,Rust似乎有一个难以操作学习的名声。很可能是因为借贷检查器和所有权的概念,它执行了内存安全。但是,现有的Rust开发者当然有不同的看法。

  • "我发现Rust远比C++容易操作。"
  • "特别是借贷检查器可能是一种痛苦,但总体而言,它要容易得多。"
  • "Rust用短期的易用性换取长期的健壮性。"

来源。Rust Discord服务器
 
我的另一位CTO联系人对Rust与Go有一个有趣的观点。关于在二者之间做出选择的问题

"肯定是Rust,特别是如果你对运送一个能正常工作的二进制文件感兴趣的话。Rust对于接近C++类型的工作来说是一个非常好的开发经验。因此,也许比Node.js对开发不那么友好,但你可以让你的DevOps流程几乎相同。另外,你可以发送一个WASM,这可能会给你一些创造性的机会,比如Chromebooks。
我对Go的体验很好,但实际上是为了将微服务运送到我控制的环境中。我对语言本身只有轻微的兴趣--感觉它在向正确的方向嗅探,但我有点觉得Rust是通过研究C++、C#、Node.js、Python和Golang来获得灵感的,并从语言、编译、目标、生态系统、社区等方面汲取了大部分的精华。"
 
一些程序员的幽默
"Rust背后的最初目的是让人们有能力自鸣得意地谈论C和C++有多么不安全,但最近它被重新利用为一种可行的系统级语言"
 
"Rust是一门语言,你试图说服编译器你的代码是正确的,最终大部分时间都会失败"
许多刚接触Rust的开发者一开始往往会在所有权上挣扎,这是Rust的突破性功能,它消除了对GC的需求;掌握了所有权,Rust将是你在内存安全方面最好的朋友。如果你忽略了所有权,你就会不断地与编译器斗争。
此外,还有借贷检查器,它做了很多事情来确保内存安全,比如确保所有变量在使用前都被初始化。它还强制要求你不能两次移动同一个值,而且你不能在借用时移动一个值。
 
"Rust是一种为Rust开发者制造的编程语言"。
编译器的独特性确实使其有别于任何其他语言,而且学习曲线最初是相当陡峭的。因此,当你开始学习Rust时,可能会很艰难,但一旦你掌握了基础知识,真正靠拢了概念,这门语言就会变得很容易使用。