当我们需要对某一个特定类型进行抽象,可以使用 struct
, 这种情况我们关注的是该类型的细节特征,比如如果对人类进行抽象,我们关注的重点会是: 年龄,职业,性别,个子高低。。。;但是如果我们并不需要关注这么多细节,只想关注人类按照性别,可以分为哪些子类,就可以使用枚举类型 enum
来列出所有的子类,实现更高级别的抽象。
枚举Enum类型的定义和实例化
enum Human {
Man,
Woman
}
定义和struct
类似,enum
后跟枚举类型名,不同的是大括号{}
里包含的是以逗号分割的各子类,而不是字段。
let man = Human::Man;
let woman = Human::Woman;
实例化时使用双冒号 “::” 指定子类
带有值的枚举Enum类型
上边我们定义的枚举类型列出了包含的所有子类,但是这些子类型不附带任何属性,如果要做一个相亲网站,对于男性,我们想要知道他的身高,对于女性我们想要知道她的身高和Cup. 到这里我们自然会想到使用struct
来装载:
struct User {
user: Human,
height: u32, // 170cm
cup: char // G
}
但是这样有一个问题,对于男性用户我们是不需要 cup 这一属性的,当然对于男性用户我们这一项可以默认指定 0,但还是感觉很别扭。
这种情况使用 Enum 来处理就优雅多了:
fn main() {
let man = Human::Man(170);
let woman = Human::Woman{height: 158, cup: 'G'};
}
enum Human {
Man(u32),
Woman{height: u32, cup: char}
}
有没有发现上边的 Man
长的非常像之前讲过的 Tuple-Struct , Woman
长的非常像常规的 struct, 而我们最开始的Human
版本中的非常像 Unit-Like Struct
我们之前说过,struct
将多个变量装载到一块,从上边的对比看 enum
非常像是将多个struct
装载到一块了。
和 struct
一样,enum
枚举类型也可以拥有方法和关联函数
为Enum添加关联函数
fn main() {
let man = Human::Man(170);
let woman = Human::Woman{height: 158, cup: 'G'};
let gay = Human::Gay;
man.info();
woman.info();
gay.info();
}
enum Human {
Man(u32),
Woman{height: u32, cup: char},
Gay
}
impl Human {
fn info(&self){
match self {
Self::Man(height) => println!("身高是:{}", height),
Self::Woman { height, cup } => println!("身高是:{},Cup是:{}", height, cup),
Self::Gay => println!("同性恋不关注身高和cup 😂")
};
}
}
Rust中没有空指针问题
java.lang.NullPointerException
如果你没有见过这个异常,那说明你一定不是一个Java程序员。
在其他编程语言中这么常见的问题,为啥Rust中就没有呢 ?
要搞清楚这个问题,首先要明白它产生的根本原因是什么。
为什么会有空指针的问题
根据我的经验,一般产生空指针的情景有:
- 从数据库中读取数据给变量时有时是空值,有时是有效值,但是写程序时没有考虑到空值的情况
- 调用函数时,返回值有时是空值,有时是有效值,但是调用函数时没有考虑到空值的情况
- 。。。
所以产生空指针的根源原因是:
当我们使用数据进行计算时,该数据有可能是空值,也有可能会是一个有效值,但是在写代码时没有考虑到空值的情况,直接按照有效值去处理,在程序运行过程中当遇到空值的情况是就会发生空指针错误
空指针的一般解决方案
一般的空指针问题是通过程序员的有意识去判断是否用到的数据有两种可能性:
- 空值
- 有效值
如果存在这样的两种可能性,需要程序员分别处理。
这个流程中有两个无法保证的关键点:
- 有意识的去判断
- 是否有两个可能性
去判断有没有两种可能性这完全是程序员的一个主观行为,有的人仔细认真,有的人粗心大意,即使仔细认真的一个程序员也有可能在哪天心情不好,也忘记了。
另外判断会不会有两种可能性,这也是一个玄学问题,有的时候一眼就能看出这个变量有可能会有空值的情况,而有的情况你很难去判断会不会有空值的情况,这个时候严谨的程序员就会为也许完全不存在的空值情况写大段代码。
所以大多数编程语言要求程序员去判断用到的每个数据是否有空值的可能性以避免空指针的解决方案,非常类似于C/C++ 要求程序员有意识判断哪些数据不再需要,然后主动释放内存。
回收内存是C/C++语言最多被人诟病的地方,而空指针也是大多数编程语言最大的痛点。
而Rust 没有给他俩留有任何余地。
Rust的解决方案
上边我们已经讲了,解决空指针问题,分为两步
- 判断一个是是否有空值和有效值两种情况
- 如果有这两种情况,需要分别处理覆盖到这两种情况
Rust 解决方案是:
- 如果一个值有空值和有效值两种情况,必须使用枚举类
Option<T>
包裹 - 使用
Option<T>
中包裹的值时强制要求覆盖空值和有效值两种情况
所以在Rust中如果一个值不是被包裹在 Option<T>
中,那么它就没有空值的可能性;如果是在Option<T>
中包裹的,要从其中取出实际值时强制要求分别处理当是空值和有效值时的情况。
所以
处理空指针在其他语言中是模糊的道德要求,在Rust中是明确清晰的法律规范
而解决空指针是一门编程语言中至关重要的存在,有时候处理不好无关痛痒,有时候会引起整个应用程序的奔溃,如果将这作为对程序员可有可无的道德要求,就像独裁国家对政府部门的是否廉洁仰仗于其自身的道德一样可笑,这也是为啥先进的民主国家将官员的财产透明公开写进法律的原因,支撑一个系统的重要支柱必须像法律条文一样被写进法律,每个人必须遵守,才会是一个健壮的系统。