什么是 Rust,为什么它如此受欢迎?
  JJk5QKT0WLay 2023年12月08日 31 0

以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/RYXiwGP1ZyTECBdG-2WHeQ

Rust连续六年成为 Stack Overflow 最受欢迎的语言,这表明许多有机会使用 Rust 的人都爱上了它。然而,大约 97% (也就是大部份)没有使用过 Rust 的调查受访者可能会想,“Rust 有什么用?”

简短的回答是,Rust 解决了许多其他语言中存在的痛点,向前迈出了坚实的一步,但缺点数量有限。

我将展示 Rust 为其他编程语言的用户提供的内容以及当前生态系统的示例。在 Rust-land 中并非全是玫瑰,所以我也谈谈缺点。正所谓人非圣人,孰能无过?

来自动态类型语言

喜欢动态类型系统还是喜欢静态类型系统的程序员之间的争论可能会持续数十年,但很难争论静态类型的好处。你只需要看看 TypeScript 等语言的兴起或 Python 的类型提示等功能,因为人们对当今更大的代码库中动态类型的当前状态感到沮丧。静态类型语言允许对数据及其行为进行编译器检查约束,从而减轻认知开销和误解。

这并不是说所有静态类型系统都是等价的。许多静态类型语言旁边都有一个大星号:它们允许 NULL. 这意味着任何值都可能是它所说的或什么都不是,有效地为每种类型创建了第二种可能的类型。

与 Haskell 和其他一些现代编程语言一样,Rust 使用可选类型对这种可能性进行编码,编译器要求你处理这种 None 情况。这可以防止可怕的 TypeError: Cannot read property 'foo' of null 运行时错误(或等效语言)的发生,而不是将其提升为编译时错误,你可以在用户看到它之前解决它。

下面是一个无论我们是否知道他们的名字都可以问候某人的函数示例;如果我们忘记了 Nonecase in thematch 或 try to use nameas if it was an always-present Stringvalue,编译器会报告警告信息。

    fn greet_user(name: Option<String>) {
        match name {
            Some(name) => println!("Hello there, {}!", name),
            None => println!("Well howdy, stranger!"),
        }
    }

Rust 的静态类型在鼓励长期可维护性的同时,尽最大努力摆脱程序员的困扰。一些静态类型的语言给程序员带来了很大的负担,要求他们多次重复变量的类型,这阻碍了可读性和重构。其他静态类型语言允许整个程序类型推断。虽然在初始开发期间很方便,但这会降低编译器在类型不再匹配时提供有用错误信息的能力。

Rust 从这两种风格中学习,并要求函数参数和常量等顶级项具有显式类型,同时允许在函数体内进行类型推断。下面在这个例子中,Rust 编译器可以根据 val 参数和返回类型声明为 32 位有符号整数来推断 twice 的类型。

    fn simple_math(val: i32) -> i32 {
        let twice = val * 2;
        twice - 1
    }

来自垃圾收集语言

使用系统编程语言的最大好处之一是能够控制低级细节。

Rust 让你可以选择将数据存储在堆栈或堆上,并在编译时确定何时不再需要内存并且可以清理内存。这允许有效地使用内存以及更高效的内存访问。

Tilde 是Skylight 产品中 Rust 的早期实际用户,结果发现他们能够通过用惯用的 Rust 重写某些 Java HTTP 端点,将内存使用量从 5GiB 减少到 50MiB 。当云提供商为增加的内存或额外的节点收取昂贵费用时,这样的节省会迅速体现价值。

无需持续运行垃圾收集器,Rust 项目非常适合通过外部函数接口被其他编程语言用作库。这允许现有项目用快速的 Rust 代码替换性能关键的部分,而没有其他系统编程语言(比如C语言)固有的内存安全风险。一些项目甚至已经使用这些技术在 Rust 中逐步重写。

通过直接访问硬件和内存,Rust 是嵌入式和裸机开发的理想语言。你可以编写极低级别的代码,例如操作系统内核或微控制器应用程序。Rust 的核心类型和功能以及可重用的库代码在这些特别具有挑战性的环境中可以大展身手。

来自其他系统编程语言

对许多人来说,Rust 在很大程度上被视为其他系统编程语言(如 C 或 C++)的替代品。与这些语言相比,Rust 可以提供的最大好处是借用检查器。这是编译器的一部分,负责确保引用不会超过它们引用的数据,它有助于消除由内存不安全引起的所有错误。

与许多现有的系统编程语言不同,Rust 不需要你将所有时间都花在细节上。Rust 努力拥有尽可能多的零成本抽象——与等效的手写代码一样高效的抽象。在这个例子中,我们展示了如何使用迭代器(一种主要的 Rust 抽象)来简洁地创建一个包含前十个平方数的向量。

    let squares: Vec<_> = (0..10).map(|i| i * i).collect();

当 safe Rust 无法表达某些概念时,你可以使用 unsafe Rust。这解锁了一些额外的权力,但作为交换,程序员现在有责任确保代码真正安全。然后可以将这种不安全的代码包装在更高级别的抽象中,以保证抽象的所有使用都是安全的。

使用不安全代码应该是经过深思熟虑的决定,因为正确使用它需要与你负责避免未定义行为的任何其他语言一样多的思考和关注。最小化不安全代码是最小化由于内存不安全导致的段错误(segment fault)和漏洞的可能性的最佳方法。

系统编程语言有一种隐含的期望,即它们将永远有效地存在。虽然一些现代开发不需要那么长的寿命,但许多企业希望知道他们的基本代码库在可预见的未来仍然可用。Rust 认识到了这一点,并围绕向后兼容性和稳定性做出了有意识的设计决策;它是为未来 40 年设计的语言。

Rust 生态系统

Rust 体验比语言规范和编译器更重要;创建和维护可生产质量软件的许多方面都被视为一等公民。可以通过 rustup 安装和管理多个并发 Rust 工具链。Rust 安装附带 Cargo,这是一个用于管理依赖项、运行测试、生成文档等的命令行工具。因为默认情况下依赖项、测试和文档可用,所以它们的使用很普遍。

crates.io 是用于共享和发现 Rust 库的社区站点。任何发布到 crates.io 的库都将在 docs.rs 上构建和发布其文档。

除了内置工具,Rust 社区还创建了大量的开发工具。基准测试、模糊测试和基于属性的测试都很容易访问并在项目中得到很好的使用。

Clippy提供额外的编译器 lints, rustfmt 提供自动惯用格式化。

IDE 支持是健康的,并且每天都在变得更加强大。

超越技术点,Rust 拥有一个充满活力、热情的社区。人们可以通过多种官方和非官方途径获得帮助,例如聊天室、用户论坛、Rust subreddit,当然还有 Stack Overflow 问答和聊天室。

Rust 有一个行为准则,由一个很棒的审核团队强制执行,以确保官方空间受到欢迎,并且大多数非官方空间也观察到类似的东西。

线下,Rust 在全球有多个协会,如 RustConf、Rust Belt Rust、RustFest、Rust Latam、RustCon Asia等。

不是所有的玫瑰

Rust 的强类型系统和对内存安全的强调——所有这些都是在编译时强制执行的——意味着在编译代码时出错是极其常见的。对于不习惯这种自以为是的编程语言的程序员来说,这可能是一种令人沮丧的感觉。

然而,Rust 开发人员花费了大量时间来改进错误消息以确保程序清晰且可操作。注意在阅读 Rust 错误时不要让你的眼睛被蒙蔽!

听到有人抱怨他们一直在“与借阅检查员作斗争”尤其常见。虽然这些错误可能令人沮丧,但重要的是要认识到,识别出的每个位置都有可能在未执行相同检查的语言中引入错误和潜在漏洞。

在此示例中,我们创建一个包含名称的可变字符串,然后引用名称的前三个字节。虽然该引用未解决,但我们试图通过清除它来改变字符串。现在不能保证引用指向有效数据并且取消引用它可能会导致未定义的行为,因此编译器会报错:

    fn no_mutable_aliasing() {
        let mut name = String::from("Vivian");
        let nickname = &name[..3];
        name.clear();
        println!("Hello there, {}!", nickname);
    }

<!---->

    error[E0502]: cannot borrow `name` as mutable because it is also borrowed as immutable
     --> a.rs:4:5
      |
    3 |     let nickname = &name[..3];
      |                     ---- immutable borrow occurs here
    4 |     name.clear();
      |     ^^^^^^^^^^^^ mutable borrow occurs here
    5 |     println!("Hello there, {}!", nickname);
      |                                  -------- immutable borrow later used here

    For more information about this error, try `rustc --explain E0502`.

有用的是,错误消息包含我们的代码并尽最大努力解释问题,指出确切位置。

由于 Rust 的静态类型特性,并且 Rust 需要覆盖 100% 的条件,而不仅仅是 99%,因此在 Rust 中制作原型解决方案可能具有挑战性。边缘情况必须有适用的代码,即使程序员还不知道快乐路径应该做什么。

在早期开发期间,通常可以通过导致程序崩溃来解决这些边缘情况,然后可以在以后添加严格的错误处理。这是一个不同于 Ruby 等语言的工作流程,在 Ruby 中,开发人员经常在 REPL 中尝试代码,然后将其移至原型,根本不考虑错误情况。

Rust 仍然相对较新,这意味着一些所需的库可能尚不可用。好处是有很多肥沃的土壤来开发这些所需的库,甚至可以利用相关计算机科学领域的最新发展。由于这一点和 Rust 的功能,Rust 的一些库(例如regex crate)是所有语言中最好的。

虽然 Rust 对稳定性和向后兼容性有着坚定的承诺,但这并不意味着该语言已经定型。一个特定的问题可能无法访问使其更易于表达甚至可能表达的语言功能。例如,Rust 已经有三年多的异步功能,但语言本身的 async/await 稳定支持只有几个月的时间。

Rust 编译器构建在 LLVM 之上,这意味着目标平台的数量将少于 C 或 C++。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月08日 0

暂无评论

推荐阅读
  qn1eRyGNKz7T   2024年02月27日   159   0   0 Rust
  qn1eRyGNKz7T   2024年02月29日   114   0   0 Rust
  qn1eRyGNKz7T   2024年03月07日   107   0   0 Rust
  qn1eRyGNKz7T   2024年03月19日   174   0   0 Rust
  qn1eRyGNKz7T   2024年02月29日   104   0   0 Rust
  qn1eRyGNKz7T   2024年03月25日   115   0   0 Rust
  qn1eRyGNKz7T   2024年04月01日   238   0   0 Rust
  qn1eRyGNKz7T   2024年03月13日   145   0   0 Rust
  3Vc6H13Lg7Nk   2024年04月28日   50   0   0 Rust
  qn1eRyGNKz7T   2024年02月23日   119   0   0 Rust
JJk5QKT0WLay