Rust基础学习-11-枚举的使用

枚举 enum,用于从众多选项中选择一个。

定义枚举

#[derive(Debug)]
enum Week {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday,
}

fn main() {
    let today = Week::Saturday;  // 使用枚举
    let tomorrow = Week::Sunday;

    println!("{:?}", today);
}

这是我们在很多面向对象语言中常见的定义枚举的方式

以往对枚举的认识,就是枚举限定了几个固定的选项,我们只能使用众多选项中的一个,或者说,只是使用了某一个元素的名字,例如上面Week枚举中的Monday,至于Monday是什么,无所谓,Monday = 1也好,Monday = 10也好,我们并不关心。但是对于Rust,我们对枚举有更进一步的应用。

用枚举代替结构体

#[derive(Debug)]
enum IpAddr {
    V4(u8, u8, u8, u8), // 这个枚举成员是四个u8类型的元祖
    V6(String), // 这个枚举成员是String类型
}

fn main() {
    // 定义一个枚举变量,并将四个值存放
    let loopbackV4 = IpAddr::V4(127, 0, 0, 1);
  
    // 定义另一个枚举变量,存入一个 String 类型的值
    // "xxx".to_string() 方法和 String::from("xxx") 是一样的效果
    let loopbackV6 = IpAddr::V6("::1".to_string());

    println!("{:?}\n{:?}", loopbackV4, loopbackV6);
}

上面的代码,我们可以给枚举的每一个成员,指定一个数据类型,并且在创建一个枚举变量的时候,将某个值存入枚举。在 struct 中可以存储不同类型的变量,现在,在枚举中也可以。

再来看一个例子

// 定义一个操作枚举
#[derive(Debug)]
enum Operation {
    Move {x: i32, y:i32},
    Jump(u32),
    Attack(i32),
}

fn main() {
    // 定义一个移动的操作
    let opt_move = Operation::Move {x: 10, y: 11};
  
    // 定义一个攻击的操作
    let opt_attack = Operation::Attack(100);
  
    // 定义一个跳跃的操作
    let opt_jump = Operation::Jump(3);

    DoOperation(opt_move);
    DoOperation(opt_attack);
    DoOperation(opt_jump);
}

// 执行操作
fn DoOperation(opt: Operation) {
    println!("Do operation: {:?}", opt);
}

上面的代码我们定义了一个 Operation 枚举,里面有移动,攻击和跳跃三种操作方式。在 main 中定义了三个操作的变量,并且将每一次操作的具体值直接附加到了枚举成员上,例如 opt_attack 攻击操作,这次操作的伤害是100。

Rust 中使用 enum 代替 struct 将获得更简洁的代码。并且,每个枚举成员可以处理不同类型和数量的数据。

Rust 的 Option 枚举解释

Rust 中没有 Null 值,无法将一个变量赋值为 Null, 例如 let a = Null;,这样的操作在Rust中不存在。但是Rust中有 Option 枚举,这个枚举,用于表示 存在不存在 的概念。有点抽象,没关系,一步一步来,先看下 Option 源代码的定义

enum Option {
  Some(T),
  None,
}

这里的 是指可以代表任何数据类型的,这是范型相关的东西,后面会学习。可以将 Option 枚举想象成可以装不同类型东西的小盒子,例如我们定义了一个装玩具汽车的小盒子,这个小盒子里只能装玩具汽车。任何时候,只要这个盒子存在,那么里面就会有两种状态,要么有玩具汽车,要么没有玩具汽车。在有些面向对象的语言中,如果访问一个玩具汽车,而恰好当时那里没有玩具汽车,那么就会造成空引用,如果没有手动处理空引用的情况,则程序就会出现Bug。而Rust则避免了 空引用 的情况。

看下面的代码

fn main(){
  // 使用 Option 将一个 String 类型的值包起来
  let name: Option = Some("Fred".to_string());
}

Option 用于某些地方可能存在有值或没值的情况。Option 及成员已经被自动包含,所以我们不需要 Option::Some(xxx) 这样来使用。

match 匹配

对于 enum 类型的值,我们不能直接比较,看下面的代码,是无法编译通过的。

 let name: Option = Option::Some("Jack".to_string());
println!(name == "Jack".to_string());

上面代码中 name == "Jack".to_string() 编译出错,因为 == 两边的数据类型不一样。这里,我们就可以用到 match

看下面的代码

#[derive(Debug)]
enum Operation {
    Move {x: i32, y:i32},
    Jump(u32),
    Attack(i32),
    Talk(String),
}

fn main() {
    let opt_talk = Operation::Talk("Hello".to_string());
    let opt_move = Operation::Move { x: 10, y: 20 };

    match opt_move {
        Operation::Talk(ref value) => { // 这里加了 ref 是为了避免所有权转移
            println!("Talk: {:?}", value);
        },
        Operation::Move {x,y} => {
            println!("Move, x: {}, y: {}", x, y);
        }
        _ => {
            // nothing
        }
    }
}

上面的代码中,match Operation 枚举时,并没有匹配所有的情况,所以最后需要 _ => ,相当于某些编译语言中 switch 中的 default,即在上面的情况都不匹配的情况下,执行的操作。

if let 使用

直接看代码,简化上面 match 的操作

if let Operation::Move{x, y} = opt_move {
    println!("Move, x: {}, y: {}", x, y);
} else {
    println!("nothing");
}

这样就可以不用 match 直接匹配枚举中的某一个成员类型。

感觉这篇博客有些地方写的可能不是很清楚,说明我对这块知识的理解程度还不够。下面是一些讲解 Rust 枚举的链接

https://rustwiki.org/zh-CN/rust-by-example/custom_types/enum.html

https://www.twle.cn/c/yufei/rust/rust-basic-enums.html

http://www.ameyalokare.com/rust/2017/10/23/rust-options.html

你可能感兴趣的:(Rust基础学习-11-枚举的使用)