Rust的面向对象(一)——结构体
从1973年C语言主体完成到1983年C++正式命名,10年的时间,编程理念经历了一次从面向过程到面向对象的蜕变。数据的组织形式也从struct到class得到了升华。自那以后,几乎所有语言的类都是class,相当一部分程序员知道怎么用class却不能准确说出来面向对象是什么。然而,Go和Rust却同时站出来说:“我们不需要class。”顿时,全场静音。
事实上,面向对象的概念早在1967年的Simula语言中就开始提出来了,比C语言的面世还早了6年。可是时至今日,全网也找不到一个权威的面向对象的定义,有的都是用会意的方式来解释什么是面向对象。
相比面向对象抽象的定义而言,面向对象的三要素却是非常具体的,它们是:
- 封装:为了增强安全性和简化编程的复杂性,隐藏对象的属性和实现细节,仅对外公开接口
- 继承:为了提高代码复用率,子类可继承父类的属性和方法
- 多态:为了消除类型之间的耦合关系,提升代码的灵活性,子类可覆盖父类的方法
封装很好理解,就是定义类的属性和方法,并且提供访问级别来控制其它类的对象是否可以访问它们。Rust虽然没有类,但可以使用结构体或枚举来定义属性,并为结构体或枚举附加方法来实现。
而继承是类之间的关系,因为没有类,Rust和Go都没有继承的概念。不过Rust巧妙的用泛型以及泛型的特性实现了一套与继承相类似的代码共享机制。
多态是建立在继承之上的,它允许在使用父类的地方使用子类,Rust的特性也拥有类似的能力。
在学习Rust的面向对象之前,需要充分意识到接下来要学习的东西与你之前认识的世界有着天壤之别,不要被过去的知识所束缚,退一步,进入一个崭新的世界。
学习Rust的面向对象是一个漫长的过程,里面要掌握的知识非常多,我们可以使用下面的路线图来学习Rust的面向对象:
结构体
Rust似乎退回到了C语言的时代,使用struct来组织数据,它的结构体分为三种:
- 一般结构体
- 元组结构体
- 类单元结构体
一般结构体
Rust的一般结构体与C语言的结构体非常相似,直接上例子:
struct Staff {
id: u8,
name: String,
email: String,
department: String,
manager_id: u8
}
fn main() {
let staff1 = Staff {
id: 1,
name: String::from("zhangmh"),
email: String::from("zhangmh@sdysit.com"),
department: String::from("开发部"),
manager_id: 1
};
println!("staff1:");
println!("id: {}, name: {}, email: {}, department: {}, manager_id: {}",
staff1.id, staff1.name, staff1.email, staff1.department, staff1.manager_id
);
let staff2 = Staff {
id: 2,
name: String::from("lixk"),
email: String::from("lixk@sdysit.com"),
department: String::from("开发部"),
manager_id: 1
};
println!("staff2:");
println!("id: {}, name: {}, email: {}, department: {}, manager_id: {}",
staff2.id, staff2.name, staff2.email, staff2.department, staff2.manager_id
);
}
对于上面的例子,staff2的department和manager_id与staff1相同,当然可以把staff1的这两个字段复制过来:
let staff2 = Staff {
id: 2,
name: String::from("yut"),
email: String::from("yut@sdysit.com"),
department: staff1.department,
manager_id: staff1.manager_id
};
可是Rust怎么会允许这么麻烦的事情存在,捷径必须有啊:
let staff2 = Staff {
id: 2,
name: String::from("yut"),
email: String::from("yut@sdysit.com"),
..staff1
};
上面代码的第5行,就可以从staff1中复制剩余的字段,够简洁吧。
如果我们要通过一个函数来创建结构体对象呢:
struct Staff {
id: u8,
name: String,
email: String,
department: String,
manager_id: u8
}
fn main() {
let staff1 = get_staff(1, String::from("zhangmh"), String::from("zhangmh@sdysit.com"), String::from("开发部"), 1
);
println!("id: {}, name: {}, email: {}, department: {}, manager_id: {}",
staff1.id, staff1.name, staff1.email, staff1.department, staff1.manager_id
);
}
fn get_staff(id: u8, name: String, email: String, department: String, manager_id: u8) -> Staff {
Staff {
id: id,
name: name,
email: email,
department: department,
manager_id: manager_id
}
}
还是那句话,get_staff函数里这么写太麻烦了,捷径,我要捷径:
fn get_staff(id: u8, name: String, email: String, department: String, manager_id: u8) -> Staff {
Staff {
id,
name,
email,
department,
manager_id
}
}
元组结构体
有一些结构体,每个字段都有约定俗成的定义,比如颜色有r、g、b,点有x、y,还叫不上名字来的,比如IP地址的四个部分。这些比较适合使用元组结构体进行存储,元组结构体与元组类似,只是多了一个类型名称:
struct IpAddr(u8, u8, u8, u8);
fn main() {
let ip = IpAddr(127, 0, 0, 1);
println!("ip: {}.{}.{}.{}", ip.0, ip.1, ip.2, ip.3);
}
类单元结构体
有没有那么一首歌,会让你轻轻跟着和。不对不对,有没有那么一种结构体,它本身什么字段也没有?Rust的答案是肯定的,这就是类单元结构体。它有什么用?它就相当于一个类,没有任何成员变量,只有一些成员函数。结构体能有成员函数?这个可以有,但不要着急,学完枚举咱们再讨论。