
16 面向对象编程
面向对象编程的几种特征:
Rust的结构体与枚举,都包含数据和方法。
rust中的impl块中提供了方法和函数。
我们可以使用 pub 关键字来决定代码中的哪些模块、类型、函数和方法是公有的,而默认情况下其他所有内容都是私有的。
如果一种语言必须拥有继承才能算作面向对象,那么 Rust 就不是这样的语言。Rust 没有办法在不借助宏的情况下,定义一个结构体去继承父结构体的字段和方法实现。
适用与老实现的代码,也能适用与新实现。这就是兼容的功效。“重用”的主要是集中控制+快速。
Rust使用trait机制来实现“重用”。
Rust使用泛型+trait约束来替代“多态”。
- 子类并不总是应该继承父类的全部特征,但使用继承时却往往会这样发生。
- 一些语言只支持单继承,这也进一步限制了程序设计的灵活性。
Trait Object 实现运行时多态
所以trait对象表示了结构体数据所以,以及该结构体的trait的所有方法所在。
trait 对象指向一个实现了我们指定 trait 的类型的实例,以及一个用于在运行时查找该类型的 trait 方法的表。
通过指定某种指针来创建 trait 对象,例如 & 引用或 Box<T> 智能指针,还有 dyn 关键字,以及指定相关的trait。
[!cite] 数据与方法解耦 6 泛型和特征
在 Rust 中,我们刻意不把结构体和枚举称为“对象”,以便把它们和其他语言里的对象区分开来。
在结构体或枚举里,字段中的数据和 impl 块里的行为是分开的,而在其他语言中,数据和行为往往会被合并进一个被称为对象的概念里。
单态化产生的代码在执行静态分发(static dispatch),也就是说编译器在编译时就知晓要调用什么方法。
这与动态分发 (dynamic dispatch)相对,这时编译器在编译时无法知晓要调用哪个方法。在动态分发的场景下,编译器会生成负责在运行时确定该调用什么方法的代码。
面向对象设计模式
函数式编程要求无状态,以保证可预测性。而对象的状态模式设计,则利用了状态,并且将每个状态所需的数据与方法打包成一个状态对象。这样整个世界都清静了。
首先每个状态对象需要提供状态转换方法,返回新的状态对象。
通常需要消费老状态, 确保了当 状态转换为新状态后不能再使用老 state 值。状态转换 方法需要获取状态值的所有权。
所以状态对象字段都需要通过Option进行包装,调用 take 方法将 state 字段中的 Some 值取出并留下一个 None ,因为Rust 不允许结构体实例中存在未初始化的字段。
