一、enum枚举
1.1 定义枚举类型和对应的数据
//定义枚举
#[derive(Debug)]
enum IpAddrKind{
IPv4,
IPv6,
}
struct Ipaddr{
kind: IpAddrKind, //设置kind为IpAddrKind的枚举类型
address: String,
}
fn route(ip_addr: &Ipaddr){
println!("ip_type = {:#?}", ip_addr.kind);
}
fn main(){
let home = Ipaddr{
kind: IpAddrKind::IPv4, //使用枚举
address: String::from("127.0.0.1"),
};
let loopback = Ipaddr{
kind: IpAddrKind::IPv6,
address: String::from("::1"),
};
route(&loopback);
route(&home);
}
上述将枚举与结构体进行绑定,显得过于麻烦,可以按照下面的简写
enum IpAddr{
IPv4(String),
IPv6(String),
}
这样简写就可以
不需要
额外使用struct- 每个数据可以拥有
不同的类型以及关联的数据量
enum IpAddr{
IPv4(String),
IPv6(String),
V4(u8,u8,u8,u8),
}
fn main(){
let home = IpAddr::IPv4(String::from("192.168.0.1"));
let loopback = IpAddr::IPv6(String::from("::1"));
let v4 = IpAddr::V4(192, 168, 0, 5);
}
1.2 再来一个比较复杂的
#[derive(Debug)]
enum Message {
Quit, //没有关联任何数据
Move { x: i32, y: i32 }, //包含一个匿名结构体
Write(String), //包含单独一个 String
ChangeColor(i32, i32, i32), //包含3个i32
}
impl Message {
fn call(&self) {
println!("self = {:#?}", self); //输出self = Write(
// "hello",
//)
}
}
fn main() {
let g = Message::Quit;
let m = Message::Move { x: 12, y: 24 };
let w = Message::Write(String::from("Hello"));
let c = Message::ChangeColor(0, 255, 255);
w.call(); //w就是call的self值
}
二、Option枚举
2.1 概念
- Option编码了一个非常普遍的场景,即
有值和没值
; - 这就意味着编译器需要检查是否处理了所有应该处理的情况;
- Rust并没有
空值(NULL)
的概念; - Rust拥有一个可以编码存在或不存在概念的枚举----
Option<T>
,T是一个泛指类型;
enum Option<T> {
Some(T),
None,
}
Option<T>
不需要将其显式引入作用域,也不需要Option::
前缀来直接使用 Some 和 None;- 如果使用 None 而不是 Some,需要告诉Rust
Option<T>
是什么类型的;
2.2 举例
1. Some和None
fn main() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
}
2. option<T>和T
- 这里的T是可以是任何类型;
option<T>
和T
是不同的类型;
下面的代码会报错
fn main() {
let some_number = Some(5); //编译器自动推断为i32类型
let som_string = Some("A String"); //编译器自动推断为字符切片(&str)类型
let absent_number:Option<i32> = None; //写None时必须显示的声明
//Option<T>和T是不同的类型, 不可以把Option<T>直接当成T
//若想使用Option<T>中的T,必须将它转换为T
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y; //^ no implementation for `i8 + Option<i8>`
}
报错信息如下
- 在对
Option<T>
进行 T 的运算之前必须将其转换为 T; - 能帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况,提高代码的安全性;
三、match模式匹配
3.1 基本用法
- match匹配允许将
一个值与一系列的模式
相比较,并根据相匹配的模式执行相应代码; - 模式可由
字面量、变量、通配符和许多其他内容
构成;
enum Coin{
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8{
match coin{
Coin::Penny => {
println!("Penny");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn main(){
value_in_cents(Coin::Penny);
}
3.2 绑定值模式
- 与在枚举成员中提取值相同,匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值;
- 下面这个例子可以修改一个枚举的成员用来存放数据,打开最终的UsState的值
#[derive(Debug)]
enum UsState{
Alabama,
Alaska,
}
enum Coin{
Penny,
Nickel,
Dime,
Quarter(UsState), //Quarter关联一个数据
}
fn value_in_cents(coin: Coin) -> u8{
match coin{
Coin::Penny => {
println!("Penny");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => { //将state绑定到Quarter所存的值上
println!("State quarter from {:?}!", state);
25
}
}
}
fn main(){
let c = Coin::Quarter(UsState::Alaska);
println!("{}", value_in_cents(c)) //State quarter from Alaska!
}
四、匹配Option<T>
- 之前使用
Option<T>
时,是为了从 Some 中取出其内部的 T 值; - 这里像处理Coin枚举那样使用match 处理
Option<T>
中的成员;
下面的函数获取一个Option<i32>
,如果其中含有一个值,将其加1;如果其中没有值,函数应该返回 None。
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
fn main() {
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
println!("five = {:#?}, six = {:#?} none = {:#?}", five, six, none)
}
打印结果
five = Some(
5,
), six = Some(
6,
) none = None
五、穷举
match匹配需要穷举,如下的代码match必须列举完0~255。
fn main() {
let x:u8 = 9;
match x {
0 => {
println!("0");
}
1 => {
println!("1");
}
}
}
代码报错如下,意思就是没有覆盖2到最大值。
这问题加一个_
通配符就能解决
fn main() {
let x:u8 = 9;
match x {
0 => {
println!("0");
}
1 => {
println!("1");
}
_ => {
println!("other");
}
}
}
六、if let
- 只关心
一种匹配
而忽略其它匹配的情况可以使用if let; - 这样也就放弃了穷举的可能;
fn main(){
let v = Some(3u8);
match v {
Some(3) => println!("three"),
_ => (),
}
//和上面的match效果相同
if let Some(3) = v {
println!("three");
}
}