Rust借用检查器优缺点


Rust借用检查器缺点:不适合原型设计和快速迭代
当你从事系统设计/架构时,Rust 的复杂性经常会使事情变慢,而当你在一个可靠的设计中实现一些确定的部分时,它通常会使事情变得更快(但如果它不可靠,它可能只会让你感到厌烦完全停止)。

换句话说,原型设计和迭代会受到借用检查的影响。有人说Rust是一种糟糕的原型设计语言,它使草稿性质的编码变得非常困难,而且其他语言的更改迭代速度要快得多。

这主要是因为与其他语言范式相比,借用检查器强加了额外的约束,例如不能对一个对象有多个可变引用的约束。当你想做改变的时候,你不能只做最简单的改变,你需要找到一个同样满足借用检查器额外约束的改变。

真正麻烦的是,当您需要更改承载接口的类型签名时,发现自己花费数小时更改每个使用该类型的地方,只是为了看看您最初的尝试是否可行。然后当你意识到你需要再次改变它时重做所有这些工作。

这是因为借用检查器的强制耦合:随着代码库的组件变得更加相互关联和耦合,那么一个组件的更改将需要另一个组件的更改。在借用检查中,每个函数都通过可变性约束与其调用者和被调用者耦合得更多。这可能与trait和一般的多态性直接冲突。

这也使更改程序的数据结构变得更糟。一位用户说,“改变它是一场噩梦,因为它迫使你重写大量代码,因为你已经稍微改变了你存储一些数据的方式”,另一位用户说, “理论上......有可能避免如果您小心安排对数据的访问权限,就可以逃生。”

这是有道理的,因为通过借用检查,我们的代码往往会与我们的数据更加耦合,因为上述强制耦合以及我们将在下面介绍的更多原因,与泄漏抽象相关。

重构似乎也存在一些问题。根据某些人的说法,改变您的程序可能很困难,就像在糖蜜中移动一样。重构可能是一个巨大的痛苦

当然,这是有道理的。借用检查器要求您在代码的每次迭代中预先满足它的约束,尤其在它变得真正重要之前。用一位用户的话说,它“迫使我本末倒置”。大多数代码在完成之前都会经历多次迭代,所以我们比其他范例更频繁地支付这个成本。

请记住,这些用户的体验并不普遍,它可能在很大程度上取决于领域。

与具有大量互连状态的域(如应用程序、有状态程序或复杂的回合制游戏)相比,命令行工具等无状态程序或嵌入式编程等低抽象​​域与借用检查器的摩擦更少。

这可能就是为什么有些人发现借用检查器使用起来更慢,而有些人发现它更快的原因。

好处:并发
借用检查器在原型设计和迭代时可能会导致开发速度变慢,但如果我们的程序使用并发,它在某些方面也有帮助。
借用检查器有助于防止数据竞争,这是指:

  • 两个或多个线程 [或异步任务] 同时访问内存位置。
  • 其中一个或多个是写入。
  • 其中一个或多个是不同步的。

读取 CPU 将获得部分、不一致的数据视图,因为写入 CPU 尚未完成写入。这些错误很难检测到,因为它们取决于线程的调度,而线程调度实际上是随机的。
这些错误可能需要几天时间才能找到,从而降低了使用并发的程序的开发人员速度。

大多数语言,包括 C、Java 和 Swift,都没有提供针对数据竞争的保护措施。Go 通过鼓励和默认消息传递来提供部分保护以防止数据竞争,但仍然会遇到偶尔的数据竞争。

并非所有程序都使用(或应该使用)并发,但如果您的程序使用并发,则借用检查器可能会提高程序这些方面的开发速度。

不支持的模式
借用检查器是一种非常有效的静态分析机制,但它仍然倾向于与许多简单、有用且安全的模式不兼容:

优势:自上而下的扁平化架构
借用检查器通常会影响我们进入自上而下的体系结构,这可以帮助我们在程序中保持假设和不变性。

简而言之,自上而下的架构是您将程序的功能组织成树(或有向无环图)的地方,这样父级可以调用子级,但子级不能调用父级。

这对您的程序来说是一个非常微妙但强大的效果。解释它本身需要整整三篇文章,但请查看Brian Will 的这段视频,他在视频中谈到了这种“程序化”风格的好处。

像 Pony 这样的 GC 语言,以及像 Haskell 和 Clojure 这样的函数式语言也有这个好处。在 Rust 中,这是其内存安全方法的自然副作用(或者更确切地说,是要求)。

借用检查器还影响我们走向“更扁平”的组织,我们所有程序的长期状态都保存在中央集合中,让人联想到关系数据库。这自然会导致某些架构,如 ECS。这种架构适用于很多程序。

请注意,它也可能不适合某些程序。更复杂的回合制游戏,例如 roguelikes,更适合其他架构。在这些情况下,ECS 不那么灵活或可扩展。

总结
MMM手工内存、借用检查、GC 和 RC 各有优缺点。然而,在开发速度这个维度上,我的一般结论是:

  • 垃圾收集通常是最好的,因为它将内存问题与手头的实际问题分离开来。
  • 引用计数几乎与垃圾收集一样好,如果使用其特定的独特能力,甚至可能更好。
  • MMM 可能非常好,只要使用适当的体系结构和工具来检测内存问题。
  • 借用检查速度较慢,但​​如果我们用 Rust 的其他方面(如RefCell )来扩充它会更好。

以下是一些粗略的指导方针,可帮助解决更具体的情况:

  • 制作游戏时,默认使用 GC 或 RC,例如 Swift 或 C#。它们提供了极快的开发速度,以及用于对性能更敏感的领域的值类型 ( struct )。如果需要更多性能,则:
    • 如果开发单人游戏,具有适当架构和内存安全缓解措施的 MMM 语言是一个不错的选择。
    • 如果开发多人游戏,那么 Rust 是一个很好的选择,因为它有助于解决安全问题,同时仍然很快。
  • 制作Web服务器时,默认使用带有RC或GC的内存安全语言(如C#),如果具有良好的并发支持,如Pony或Go,则加分。
    • 如果处理对延迟极其敏感的事情,Rust 可能会更好。对于暴露在网络中的东西,MMM 并不是真正可取的。
  • 在制作移动应用程序时,请坚持使用 Swift、Kotlin 或 Typescript。GC 在这里特别好,因为它的暂停可能发生在用户操作之间。
  • 如果从事需要高可靠性和正确性的工作,请查看 Pony 之类的。它的运行时保证永远不会导致用户未明确创建的运行时错误。


更多点击标题