# Rust语言速成

> 学习Rust最好的方式，就是拿它写个OS。 ——作者

Rust语言独具特色，然而学习曲线十分陡峭。在你享受到它为系统编程带来的好处之前，很可能直接被它编译器严格的检查机制所劝退。

为了帮助大家尽快地上手Rust，这里简要列出了我个人认为合理地学习顺序，供大家参考。这里我们假设读者已经具有一定C语言编程经验。

#### 存活阶段

* 工具链基础使用方法
* Rust基础语法：
  * 变量、函数、数据类型、控制流
  * 数组、字符串、结构体、元组、枚举
* unsafe：指针操作

至此，你应该能够用Rust写等价的C代码了！

#### 入门阶段

* Rust内存模型：从此告别SegmentFault
  * 可变性
  * 所有权，移动语义
  * 生命周期和借用
* Rust高级语法：现代语言特性
  * 模式匹配
  * 闭包/lambda表达式，函数式编程
* 面向对象：Better C with Class
  * 成员变量
  * 接口：Trait，Trait对象（动态分派）
  * 泛型（静态分派）
* 核心库：你要的轮子，都在这里
  * Option、Result、错误处理机制
  * 容器类：Vec、VecDeque、BTreeSet、BinaryHeap……
  * 迭代器、链式调用
  * 堆分配，智能指针：Box、Rc、Arc……
  * 消灭unsafe：运行时借用检查RefCell
  * 了解其它常用Trait
* 项目管理：工程化与模块化
  * 模块系统，文件层次
  * 包管理系统，项目配置文件Cargo.toml

至此，你应该开始体会到Rust为系统编程带来的便利了！

#### 高级阶段

* 宏
* 反射
* 安全并发机制
* 与C语言互操作
* 内联汇编

## Rust样例

这里给出一些需求的最简单实现。代码位于：[RustSamples](https://github.com/wangrunji0408/RustSamples)。

目前包括以下功能样例：

* Rust调用C
* C调用Rust

## 值得一提的特点

### 大量泛型

如果你阅读过一些Rust代码就会发现，**泛型**这一特性被广泛运用，频率高于其它任何一门常用语言。在C++中，泛型/模板是晦涩和强大的代名词，而在Rust中，它平凡而常见。

其实，Rust中的泛型大部分只是为了解决一个简单的需求——**调用接口**。

举个例子，我们要定义一个接口I，并在一个函数中使用它。在C++中，我们会使用**基类指针**：

```cpp
abstract class I {
    int func();
}

int call_func(I* p) {
    return p->func();
}
```

在Rust中其实也有类似的事物——[**Trait对象**](https://kaisery.github.io/trpl-zh-cn/ch17-02-trait-objects.html#trait-%E5%AF%B9%E8%B1%A1%E6%89%A7%E8%A1%8C%E5%8A%A8%E6%80%81%E5%88%86%E5%8F%91)：

```rust
trait I {
    fn func(&self) -> i32;
}

fn call_func(p: Box<I>) -> i32 {
    p.func()
}
```

但它并不常用。我们注意到p的类型是`Box<I>`，意味着传入对象必须是分配在堆上。如果把这里改成`I`，就会出现以下错误：

> the trait bound `I: std::marker::Sized` is not satisfied \[E0277] `I` does not have a constant size known at compile-time

Rust文档中也具体说明了[Trait对象的使用条件——对象安全](https://kaisery.github.io/trpl-zh-cn/ch17-02-trait-objects.html#trait-%E5%AF%B9%E8%B1%A1%E8%A6%81%E6%B1%82%E5%AF%B9%E8%B1%A1%E5%AE%89%E5%85%A8)。

由于以上方法用起来有诸多不便，因此我们常用**泛型**实现接口调用：

```rust
// T是一个实现了trait I的类型
fn call_func<T: I>(p: T) -> i32 {
    p.func()
}

// 当<>中的内容比较多时，
// 也可以把泛型约束放在外面
fn call_func<T>(p: T) -> i32 
    where T: I 
{ p.func() }
```

其实用泛型的方法实现接口调用在C++中也可以实现，但由于目前C++缺乏泛型约束（C++20的标准：Concept），导致传入错误类型后编译器会吐出一堆垃圾。很少有人使用。

使用泛型相比基类指针还有一个好处：性能更好。使用基类指针调用接口函数时，需要**在运行时查虚表**，而用泛型就能**在编译时确定最终类型**，节省一次查表开销，编译器还能做更多内联优化。前者称为**动态分派**，后者称为**静态分派**。但如果对象类型只能在运行时确定，那就必须用前者了。

### 没有继承

Rust作为一个面向对象语言，竟然没有继承？！

是的！很多新兴的编程语言都逐渐抛弃了继承这一特性，比如和它相爱相杀的Go。

回忆OOP里面诸多的设计原则，其中有一条就是：**使用组合代替继承**。其原因就是我们总会继承一些多余的东西，造成类间高度的耦合性。最早的C++最复杂，之后的Java做了一些简化（抽象出接口，只允许单继承），不过还不够彻底。到了Rust这代语言，继承被彻底抛弃，只保留了接口。

不过，Rust也提供了一个代替方案：[**Deref特性**](https://kaisery.github.io/trpl-zh-cn/ch15-02-deref.html)。使用方法是把“父类”当作“子类”的一个内部变量，Deref提供了一个语法糖来自动使用其“父类”的成员。

关于OOP和继承的讨论，推荐阅读[这篇知乎回答](https://www.zhihu.com/question/31377101/answer/403757577)，以及[Rust文档中的继承](https://kaisery.github.io/trpl-zh-cn/ch17-01-what-is-oo.html#a%E7%BB%A7%E6%89%BF%E4%BD%9C%E4%B8%BA%E7%B1%BB%E5%9E%8B%E7%B3%BB%E7%BB%9F%E4%B8%8E%E4%BB%A3%E7%A0%81%E5%85%B1%E4%BA%AB)。

## 参考资料

* [Rust程序设计语言 中文版](https://kaisery.github.io/trpl-zh-cn/)
* [Rust官方文档库](https://www.rust-lang.org/zh-CN/documentation.html)
