0. Rust速成课介绍

我宣布即将发布的一系列博客帖子,我称之为“Rust速成课”。 在这篇博文中,我想解释一下:

  • 我为什么对 Rust 感兴趣

  • 我为什么要写这个系列

  • 这个系列是写给谁的

  • 我为这个系列准备的口味

  • 我想讲一些 Rust 的例子

由于 FP Complete 使用 Rust 的量增加,因此我将继续从事本系列的工作。

为什么是 Rust?

我非常相信使用编译器来帮助消除 Bug。 没有一种编程语言可以消除所有的 Bug,即使是最好的设计语言也需要给开发人员留下足够的回旋余地。 不过,使用这种语言的项目在安全性和长期可维护性方面仍具有重要价值。

大约十年来,我使用的主要语言一直是 Haskell (现在仍然是)。 这里有很多东西,比如不变性、显式效果和强类型。 但是对 Haskell 的高度关注给我带来了两个问题:

  • 总是可以从其他语言中学到一些东西。 人们很容易陷入精通一门语言的陷阱,并对其缺点视而不见。 现在 Haskell 是我使用时间最长的主要语言,最终取代了 Java。 我需要避开这个陷阱。

  • Rust 有一些合法的技术优势

    • 性能更好,也更可靠,更少依赖重写规则之类的东西

    • 对于移动和前端开发,有一个更好的前景

    • 垃圾收集的缺乏为实时开发开辟了新的可能性

    • Rust在某些地方提高了Haskell的安全性,例如默认要求完全匹配模式

像许多其他人一样,多年来我一直听到关于 Rust 的传言。 大约两年前,我开始更多地使用它,并且一直在稳步地投入更多的个人时间来使用它。 最近我们对 Rust 的工作越来越感兴趣,因此,我们一直在扩大我们的内部 Rust 团队。

为什么要写这个系列?

我们是 FP Complete 的一个全球分布式团队,我们在任何可能的地方大力推动使用书面交流工具。 这也与培训材料严重重叠。 随着我(在内部和客户方面)进行更多的培训,我发现了以下地方:

  • 我发现自己对这种语言缺乏了解

  • 新来者也很艰难

本系列文章目的是收集有关如何开始使用 Rust 的入门指南,以及关于如何避免陷入困境的刻苦教训。其中一些绊脚石可能会吸引我直接与之合作的受众群体(很多Haskell和DevOps工程师),但是随着时间的流逝,这种情况可能会发生变化。

读者对象

我正在为这个系列的 Rust 好奇。 假设自己具有编程知识,以及有关 Rust 的一些基本概念,但对语言本身没有真正的了解。 我将尽力让您在阅读 Rust 书籍时提出要求。

如果您是 Java 用户,Rubyist或Haskeller,而Rust吸引了您,希望对您有所帮助。 甚至 Rustaceans 也会喜欢看到我发现自己和其他人正在遭受的痛苦。

系列的味道

rust-lang.org 上已经有很好的材料了。 我并不打算试图取代它。 相反,我假设大家正在阅读 《Rust》 这本书,并指引适当的章节。

一个具体的例子:我不想花很多时间谈论Rust语法,也不打算说明它是一种面向表达式的语言。 这本书涵盖了这一点。

相反,我想给大家:

  • 可以查看和使用的真实代码

  • 用简单的问题来积累经验

  • 人们对棘手案例的解释

并注意...

Rust 陷阱

推特上有几个人请我分享一些 Rust 陷进,特别是从 Haskell 开发者的角度来看。 以后我肯定会讲到更多的问题,但是我想在第一篇文章中给出一些例子,这样你就可以对我们在本系列文章中讨论的问题有所了解。 我不打算在这里详细解释这些问题,这也是这个系列文章的目的!

正如你所知道的:除了下面的 Disqus 评论之外,这些例子没有任何内容。 如果你对这些陷阱不感兴趣,现在就可以放弃阅读,敬请继续关注更多的帖子。

可变值,可变变量

Haskell 有一个简单的思维模型。 值都是不可变的, 一些特殊的引用类型使我可以更改其内容。 这样的突变在类型系统中被追踪。

从这个角度来看,Rust 有点令人惊讶,这里有一个例子:

fn main() {
    let i: isize = 1;
    let j: isize = foo(i);
    println!("{}", j);
}

fn foo(mut i: isize) -> isize {
    i += 1;
    i
}

等一下... 我是不可变的。 然后我把它传递给 foo,它就变得可变了。 然后将这个可变值作为不可变值返回。 什么?

我向你保证,这最终是有道理的,但是有点令人惊讶。 另外,x: &mut isize 和 mut x: &mut isize 都是真实意味着不同的东西:

fn main() {
    let mut i: isize = 1;
    let mut j: isize = 2;
    foo(&mut i, &mut j);
    println!("{} {}", i, j);
}

fn foo<'a>(mut i: &'a mut isize, j: &'a mut isize) {
    *i *= 10;
    i = j;
    *i *= 10;
}

如此多的 strings

有人警告我要注意它,我把它忽略掉了。我对自己说,一个 Haskeller,在字符串,严格的文本,懒惰的文本,字节字符串和更多的艺术方面受过训练,是不可能被吓倒的。 然而,我错了。

fn main() {
    let hello1 = String::from("Hello, ");
    let hello2 = String::from(", hello!");
    let name = "Alice";
    println!("{}", hello1 + name);
    println!("{}", name + hello2);
}

不,上面的代码不能编译。

魔法般地起作用,直到它不起作用

在Rust中,,以人体工程学的名义发生了许多“神奇”的事情。 通常情况下,它们工作得非常完美,避免了许多挫折。 有时候,他们会失败。 看看这个破碎的代码:

fn main() {
    for arg in std::env::args().skip(1) {
        respond(arg);
    }
}

fn respond(arg: &str) {
    match arg {
        "hi" => println!("Hello there!"),
        "bye" => println!("OK, goodbye!"),
        _ => println!("Sorry, I don't know what {} means", arg),
    }
}

你会得到一个错误消息:

expected &str, found struct `std::string::String`

哦,这就说得通了! 我需要获得一个 str 的引用,而不是从 args() 获得的 String。 很容易解决:

respond(&arg);

但是后来我意识到这个响应函数很愚蠢,并且把匹配内置了:

fn main() {
    for arg in std::env::args().skip(1) {
        match &arg {
            "hi" => println!("Hello there!"),
            "bye" => println!("OK, goodbye!"),
            _ => println!("Sorry, I don't know what {} means", arg),
        }
    }
}

我想起来了,我用的是 &arg 而不是 arg,所以你可能会觉得这样很好,但事实并非如此:

  |             ^^^^ expected struct `std::string::String`, found str
  |
  = note: expected type `&std::string::String`
             found type `&'static str`

真奇怪。 为了弄清楚这里到底发生了什么,你必须了解相当多的 deref 魔法背后的一些细节。 我敢说有些魔法甚至可以称得上是抽象漏洞定律。 (别担心,这仍然是零成本。) 你可以用以下两种方法来解决这个问题:

match &*arg {

match arg.as_ref() {

将数据转移到闭包

到目前为止,我在 “Rust漫长而充满风险的历程” 中遇到的最大痛点就是将数据全部移动到闭包状态。 我已经被 Haskell 宠坏了:我喜欢不断地使用闭包,并且习惯于垃圾收集让它神奇地工作。

稍后我将展示一些真正的令人挠头的东西,不过这个相对简单的例子很有意思:

fn main() {
    let hello = String::from("Hello, ");
    let greet = |name| hello + name;
    println!("{}", greet("Alice"));
    println!("{}", greet("Bob"));
}

最后更新于