rust_os_docs
Search
⌃K

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
目前包括以下功能样例:
  • Rust调用C
  • C调用Rust

值得一提的特点

大量泛型

如果你阅读过一些Rust代码就会发现,泛型这一特性被广泛运用,频率高于其它任何一门常用语言。在C++中,泛型/模板是晦涩和强大的代名词,而在Rust中,它平凡而常见。
其实,Rust中的泛型大部分只是为了解决一个简单的需求——调用接口
举个例子,我们要定义一个接口I,并在一个函数中使用它。在C++中,我们会使用基类指针
abstract class I {
int func();
}
int call_func(I* p) {
return p->func();
}
在Rust中其实也有类似的事物——Trait对象
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对象的使用条件——对象安全
由于以上方法用起来有诸多不便,因此我们常用泛型实现接口调用:
// 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特性。使用方法是把“父类”当作“子类”的一个内部变量,Deref提供了一个语法糖来自动使用其“父类”的成员。
关于OOP和继承的讨论,推荐阅读这篇知乎回答,以及Rust文档中的继承

参考资料