Rust的面向对象编程特性-面向对象语言的特征
对象包含数据和行为
The Gang of Four,它是面向对象编程模式的目录。它这样定义面向对象编程:
面向对象的程序是由对象组成。一个对象包含数据和操作这些数据的过程。这些过程通常被称为方法或操作。
在这个定义下,Rust是面向对象的:结构体和枚举包含数据而impl块提供了在结构本和枚举之上的方法。虽然带有方法的结构体和枚举并不被称为对象,但是他们提供了与对象相同的功能。
封装隐藏了实现细节
另一个通常与面向对象编程相关的方面是封装 (encapsulation) 的思想:对象的实现细节不能被使用对象的代码获取到。所以唯一与对象交互的方式是通过对象提供的公有api;使用对象的代码无法深入到对象内部并直接改变数据或者行为。封装使得改变和重构对象的内部时无需改变使用对象的代码。
可以使用pub关键字来决定模块、类型、函数和方法是公有的,而默认情况下其它一切都是私有的。
示例1有AveragedCollection结构体的定义,文件名:src/lib.rs:
pub struct AveragedCollection { list: Vec<i32>, average: f64, }
注意,结构体自身被标记为pub,这样其他代码就可以使用这个结构体,但是在结构体内部的字段仍然是私有的。这是非常重要的,因为我们希望保证变量被增加到列表或被从列表删除时,也会同时更新平均值。可以通过在结构体上实现add、remove、average方法做到这一点。如示例2所示:
impl AveragedCollection { //类似构造函数(关联函数, associated functions) pub fn build_averaged(list: Vec<i32>, average: f64) -> AveragedCollection { AveragedCollection { list, average, } } pub fn add(&mut self, value: i32) { self.list.push(value); self.update_average(); } pub fn remove(&mut self) -> Option<i32> { let result = self.list.pop(); match result { Some(value) => { self.update_average(); Some(value) } None => None, } } pub fn average(&self) -> f64 { self.average } fn update_average(&mut self) { let total: i32 = self.list.iter().sum(); self.average = total as f64 / self.list.len() as f64; } }
list和average是私有的,所以没有其它方式来使得外部的代码直接向list增加或者删除元素,否则list改变时可能会导致average字段不同步。average方法返回average字段的值,这使得外部的代码只能读取average而不能修改它。
如果封装是一个语言被认为是面向对象语言所必要的方面的话,那么Rust满足这个要求。在代码中不同的部分使用pub与否可以封装其实现细节。
继承,作为类型系统与代码共享
继承 (Inheritance)是一个很多编程语言都提供的机制,一个对象可以定义为继承另一个对象的定义,这使其可以获得父对象的数据和行为,而无需重新定义。
如果一个语言必须有继承才能被称为面对象语言的话,那么Rust就不是面向对象的。无法定义一个结构体继承父结构体的成员和方法。然而,Rust也提供了其它的解决方案。
选择继承有两个主要的原因:
- 第一个是为了重用代码:一旦为一个类型实现了特定行为,继承可以对一个不同的类型重用这个实现。相反Rust代码可以使用默认trait方法实现来进行共享。
- 第二个是使用继承的原因与类型系统有关:表现为子类型可以用于父类型被使用的方法。这也被称为多态(polymorphism),这意味着如果多称对象共享特定的属性,则可以相互替代使用。
多态(Polymorphism)
很多人将多态描述为继承的同义词。不过它是一个有关可以用于多种类型的代码的更广泛的概念。对于继承来说,这些类型通常是子类。 Rust 则通过泛型来对不同的可能类型进行抽象,并通过 trait bounds 对这些类型所必须提供的内容施加约束。这有时被称为 bounded parametric polymorphism。
近来继承作为一种语言设计的解决方案在很多语言中失宠了,因为其时常带有共享多于所需的代码的风险。子类不应总是共享其父类的所有特征,但是继承却始终如此。如此会使程序设计更为不灵活,并引入无意义的子类方法调用,或由于方法实际并不适用于子类而造成错误的可能性。某些语言还只允许子类继承一个父类,进一步限制了程序设计的灵活性。
因为这些原因,Rust 选择了一个不同的途径,使用 trait 对象而不是继承。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2020-10-07 1027. Longest Arithmetic Subsequence (Solution 1)
2020-10-07 346. Moving Average from Data Stream