Rust 枚举

枚举用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。

Rust 语言提供了 enum 关键字用于定义枚举。

 

1. Rust 枚举定义的语法

enum enum_name {
   variant1,
   variant2,
   variant3
}

例如,我们可以定义一个水果名称的枚举 Fruits :

enum Fruits {
    Banana,     // 香蕉
    Pear,       // 梨
    Mandarin,   // 橘子
    Eggplant    // 茄子
}

 

2. Rust 枚举的使用

枚举定义好了之后我们就要开始用它了,枚举的使用方式很简单,就是 枚举名::枚举值。语法格式如下:

enum_name::variant

上面的范例中,假如我们选择了 香蕉,那么赋值的语法如下:

let selected = Fruits::Banana;

如果需要明确指定类型,可以如下:

let selected: Fruits = Fruits::Banana;

下面的范例,演示了枚举类型的基本使用方法:

我们首先定义了一个枚举 Fruits ,它有四个枚举值,分别是 Banana、Pear、 Mandarin 和 Eggplant,然后使用 println!() 输出枚举值。

注意:关于枚举前面的 #[derive(Debug)] 我们后面会介绍。

#[derive(Debug)]
enum Fruits {
    Banana,     // 香蕉
    Pear,       // 梨
    Mandarin,   // 橘子
    Eggplant    // 茄子
}

fn main() {
   let selected = Fruits::Banana;
   println!("{:?}",selected);
}

编译运行以上 Rust 代码,输出结果如下:

Banana

 

3. #[derive(Debug)] 注解

想必大家看到了 enum Fruits 前面的 #[derive(Debug)]。

这个 #[derive(Debug)] 语句的作用是啥呢 ?

我们先把 #[derive(Debug)] 去掉看看效果:

enum Fruits {
    Banana,     // 香蕉
    Pear,       // 梨
    Mandarin,   // 橘子
    Eggplant   // 茄子
}

fn main() {
   let selected = Fruits::Banana;
   println!("{:?}",selected);
}

编译上面的 Rust 代码报错,错误信息如下:

error[E0277]: `Fruits` doesn't implement `std::fmt::Debug`
  --> src/main.rs:11:20
   |
11 |    println!("{:?}",selected);
   |                    ^^^^^^^^ `Fruits` cannot be formatted using `{:?}`
   |
   = help: the trait `std::fmt::Debug` is not implemented for `Fruits`
   = note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug`
   = note: required by `std::fmt::Debug::fmt`

这段错误的意思,就是我们的 enum Fruits 枚举并没有实现 std::fmt::Debug 特质 ( trait )。

关于 特质 trait 我们会在后面介绍,这里你只要把特质当作接口 interface 看待就好。

为了让编译能通过,我们需要将我们的枚举派生自或衍生自一个已经实现了 std::fmt::Debug 特质的东西。这个东西比较常见的就是 Debug 。

因此 #[derive(Debug)] 注解的作用,就是让 Fruits 派生自 Debug。

但是,即使添加了 #[derive(Debug)] 注解仍然会有警告:

#[derive(Debug)]
enum Fruits {
    Banana,     // 香蕉
    Pear,       // 梨
    Mandarin,   // 橘子
    Eggplant   // 茄子
}

fn main() {
   let selected = Fruits::Banana;
   println!("{:?}",selected);
}

编译结果如下:

warning: variant is never constructed: `Pear`
 --> main.rs:4:5
  |
4 |     Pear,       // 梨
  |     ^^^^
  |
  = note: #[warn(dead_code)] on by default

warning: variant is never constructed: `Mandarin`
 --> main.rs:5:5
  |
5 |     Mandarin,   // 橘子
  |     ^^^^^^^^

warning: variant is never constructed: `Eggplant`
 --> main.rs:6:5
  |
6 |     Eggplant   // 茄子
  |     ^^^^^^^^

意味着:那些我们没用到的枚举都还没有被构造。

 

4. Option 枚举

Rust 语言核心和标准库内置了很多枚举,其中有一个枚举我们会经常和它打交道,那就是 Option 枚举。

Option 枚举代表了那种 可有可无 的选项。它有两个枚举值 None 和 Some(T)。

  • None 表示可有可无中的
  • Some(T) 表示可有可无中的 ,既然有,那么就一定有值,也就是一定有数据类型,那个 T 就表示有值时的值数据类型。

Option 枚举的定义代码如下

enum Option<T> {
   Some(T),      // 用于返回一个值 used to return a value
   None          // 用于返回 null ,虽然 Rust 并不支持 null 
   the null keyword
}

Rust 语言并不支持 null 关键字,取而代之的是使用 None 作为没有的意思。

Option 枚举经常用在函数中作为返回值,因为它可以表示有返回且有值,也可以用于表示有返回但没有值。

如果函数有返回值,那么可以返回 Some(data),如果函数没有返回值,则可以返回 None

如果你还不理解,那么就直接看范例吧

范例

下面的范例,我们定义了一个函数 is_even(),使用 Option 枚举作为它的返回值类型。

如果传递给 is_even() 函数的参数是个偶数,则返回传递的参数,如果是奇数则返回 None。

fn main() {
   let result = is_even(3);
   println!("{:?}",result);
   println!("{:?}",is_even(30));
}
fn is_even(no:i32)->Option<bool> {
   if no %2 == 0 {
      Some(true)
   } else {
      None
   }
}

编译运行以上 Rust 代码,输出结果如下

None
Some(true)

 

5. match 语句和枚举

枚举 的另一个重要操作就是判断枚举值。判断一个枚举值,== 比较运算符是不起作用的。

准确的说会报错

#[derive(Debug)]
enum Fruits {
    Banana,     // 香蕉
    Pear,       // 梨
    Mandarin,   // 橘子
    Eggplant   // 茄子
}

fn main() {
    let selected = Fruits::Banana;
    if selected == Fruits::Banana {
        println!("你选择了香蕉");
    } else {
        println!("你选择了其它");
    }
   println!("{:?}",selected);
}

编译错误

  --> src/main.rs:11:17
   |
11 |     if selected == Fruits::Banana {
   |        -------- ^^ -------------- Fruits
   |        |
   |        Fruits
   |
   = note: an implementation of `std::cmp::PartialEq` might be missing for `Fruits`

判断一个枚举变量的值,唯一能用的操作符就是 match 语句。

match 语句我们之间已经学过了,我们就不介绍了。我们直接上范例,看看如何使用 match 语句来判断枚举值

范例

下面的代码,我们定义了一个枚举 CarType,同时定义了一个函数 print_size() ,它接受 CarType 枚举类型的变量,并使用 match 语句来判断枚举变量的值。

enum CarType {
   Hatch,
   Sedan,
   SUV
}
fn print_size(car:CarType) {
   match car {
      CarType::Hatch => {
         println!("Small sized car");
      },
      CarType::Sedan => {
         println!("medium sized car");
      },
      CarType::SUV =>{
         println!("Large sized Sports Utility car");
      }
   }
}
fn main(){
   print_size(CarType::SUV);
   print_size(CarType::Hatch);
   print_size(CarType::Sedan);
}

编译运行以上 Rust 代码,输出结果如下

Large sized Sports Utility car
Small sized car
medium sized car

 

6. match 语句和 Option 类型

既然 match 语句可以用于比较和判断枚举变量的值,那么对于上面提到的 Option 枚举,match 语句也使用。

事实上,match 语句和 Option 类型相结合才能体现出 Option 枚举的强大之处

fn main() {
   match is_even(5) {
      Some(data) => {
         if data==true {
            println!("Even no");
         }
      },
      None => {
         println!("not even");
      }
   }
}
fn is_even(no:i32)->Option<bool> {
   if no%2 == 0 {
      Some(true)
   } else {
      None
   }
}

编译运行以上 Rust 代码,输出结果如下

not even

 

7. match 语句和带数据类型的枚举

Rust 中的枚举值可以有它们自己的数据类型。这种枚举值带数据类型的语法,彻底把 match 语句和 枚举 推向了史无前例的高度。

更厉害的是,每一个枚举值可以有不同的数据类型。

枚举值带数据类型的语法格式很简单,如下

enum enum_name {
   variant1(data_type1),
   variant2(data_type2),
   variant3(data_type1)
}

例如下面的枚举

enum GenderCategory {
   Name(String),
   Usr_ID(i32)
}

枚举 GenderCategory 有两个枚举值 Name 和 User_ID,它们有着不同的数据类型:String 和 i32。

范例

下面的范例,我们定义了一个带数据类型的枚举 GenderCategory。 然后演示了带数据类型枚举值的初始化和 match 语句的判断

// 省的报错
#[derive(Debug)]
enum GenderCategory {
   Name(String),Usr_ID(i32)
}
fn main() {
   let p1 = GenderCategory::Name(String::from("Mohtashim"));
   let p2 = GenderCategory::Usr_ID(100);
   println!("{:?}",p1);
   println!("{:?}",p2);

   match p1 {
      GenderCategory::Name(val)=> {
         println!("{}",val);
      }
      GenderCategory::Usr_ID(val)=> {
         println!("{}",val);
      }
   }
}

编译运行以上 Rust 代码,输出结果如下

Name("Mohtashim")
Usr_ID(100)
Mohtashim

Rust 向量(Vec):Rust 向量是一个长度可变的数组,它使用一段连续的内存块存储相同类型的元素。Rust 向量与数组不同之处在于:向量的长度是可变的,可以在运行时增长或者缩短,而数组的长度是固定不变的。Rust 向量以特定顺序(添加顺序)将数据存储为元素序列。向量中的每个元素都分配有唯一的索引号。