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++中,我们会使用基类指针:
在Rust中其实也有类似的事物——Trait对象:
但它并不常用。我们注意到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对象的使用条件——对象安全。
由于以上方法用起来有诸多不便,因此我们常用泛型实现接口调用:
其实用泛型的方法实现接口调用在C++中也可以实现,但由于目前C++缺乏泛型约束(C++20的标准:Concept),导致传入错误类型后编译器会吐出一堆垃圾。很少有人使用。
使用泛型相比基类指针还有一个好处:性能更好。使用基类指针调用接口函数时,需要在运行时查虚表,而用泛型就能在编译时确定最终类型,节省一次查表开销,编译器还能做更多内联优化。前者称为动态分派,后者称为静态分派。但如果对象类型只能在运行时确定,那就必须用前者了。
没有继承
Rust作为一个面向对象语言,竟然没有继承?!
是的!很多新兴的编程语言都逐渐抛弃了继承这一特性,比如和它相爱相杀的Go。
回忆OOP里面诸多的设计原则,其中有一条就是:使用组合代替继承。其原因就是我们总会继承一些多余的东西,造成类间高度的耦合性。最早的C++最复杂,之后的Java做了一些简化(抽象出接口,只允许单继承),不过还不够彻底。到了Rust这代语言,继承被彻底抛弃,只保留了接口。
不过,Rust也提供了一个代替方案:Deref特性。使用方法是把“父类”当作“子类”的一个内部变量,Deref提供了一个语法糖来自动使用其“父类”的成员。
关于OOP和继承的讨论,推荐阅读这篇知乎回答,以及Rust文档中的继承。
参考资料
Last updated