枚举应用

下面学习一下枚举的三种使用方式

1 )use 配合枚举

use 导入枚举变体

// 消除未使用代码的编译警告
#![allow(dead_code)]

enum Stage {
    Beginner,
    Advanced,
}

enum Role {
    Student,
    Teacher,
}

fn main() {
    // 显式导入 Stage 下的两个变体(Beginner、Advanced),不用写 Stage::
    use Stage::{Beginner, Advanced};

    // 通配符 *:导入 Role 内部**所有变体**
    use Role::*;

    let stage = Beginner; // 等价 Stage::Beginner
    let role = Student;   // 等价 Role::Student

    match stage {
        Beginner => println!("初学者开启学习之旅!"),
        Advanced => println!("进阶学习者正在深耕知识..."),
    }

    match role {
        Student => println!("学生正在汲取知识!"),
        Teacher => println!("老师在传播知识!"),
    }
}Code language: Rust (rust)

输出结果

初学者开启学习之旅!
学生正在汲取知识!
  1. use 枚举::{变体1,变体2}:选择性导入指定枚举成员;
  2. use 枚举::*:通配导入枚举全部变体;
  3. 不使用 use 时,必须完整书写 Stage::Beginner

2)C 风格枚举

#![allow(dead_code)]

// 隐式判别值:从 0 开始自动递增
enum Number {
    Zero,  // 0
    One,   // 1
    Two,   // 2
}

// 显式手动指定判别值
enum Color {
    Red = 0xff0000,
    Green = 0x00ff00,
    Blue = 0x0000ff,
}

fn main() {
    // C风格枚举可以直接强转整数
    println!("zero is {}", Number::Zero as i32);
    println!("one is {}", Number::One as i32);

    println!("roses are #{:06x}", Color::Red as u32);
    println!("violets are #{:06x}", Color::Blue as u32);
}Code language: PHP (php)

输出结果

zero is 0
one is 1
roses are #ff0000
violets are #0000ffCode language: CSS (css)
  • 只有无数据的单元变体枚举才能强转整型;
  • 自动赋值:第一个变体默认0,后续依次+1;
  • Rust 枚举和 C 枚举本质不同:普通 Rust enum 可以携带数据,C风格只是特例。

如果枚举包含元组/结构体变体,无法 as 转换成整数

经典案例

用 Enum + Box 实现单向链表

#![allow(dead_code)]

use crate::List::*;

enum List {
    // Cons(元素, 指向下一节点的堆指针)
    Cons(u32, Box<List>),
    // Nil:链表终止标记(空节点)
    Nil,
}

// 给枚举 List 绑定方法
impl List {
    // 创建空链表
    fn new() -> List {
        Nil
    }

    // 在链表头部插入元素,返回新链表
    fn prepend(self, elem: u32) -> List {
        Cons(elem, Box::new(self))
    }

    // 获取链表长度
    fn len(&self) -> u32 {
        match *self {
            // ref tail:借用尾部节点,不获取所有权
            Cons(_, ref tail) => 1 + tail.len(),
            Nil => 0
        }
    }

    // 序列化为字符串输出
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                format!("{}, {}", head, tail.stringify())
            },
            Nil => format!("Nil")
        }
    }
}

fn main() {
    let mut list = List::new();

    // 头部追加元素(后进先出)
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    println!("链表长度: {}", list.len());
    println!("{}", list.stringify());
}Code language: Rust (rust)

运行输出

链表长度: 3
3, 2, 1, Nil

说明

  1. 为什么要用 Box<List>
   Cons(u32, List) // 非法!递归类型无限大小
   Cons(u32, Box<List>) // Box 是堆上指针,固定大小Code language: PHP (php)

Rust 需要在编译期确定类型大小,直接递归List会无限嵌套;Box<T>把数据放到堆,栈只存指针,解决尺寸问题。

  1. prepend(self):获取链表所有权
    每次插入会消耗旧链表,构建新节点,属于不可变链表(函数式链表),不是传统可变链表。
  2. match *self + ref tail
    self 类型是 &List(借用),解引用 *self 匹配;
    使用 ref tail 只借用内部数据,不抢夺所有权,保证 .len() 可以多次调用。
  3. 缺陷提示
    当前 len()stringify()递归实现,链表很长时会触发栈溢出;生产环境一般改用循环迭代。

总结

  1. 枚举变体三类形式
   enum Foo {
       Unit,               // 单元变体(C风格)
       Tuple(i32, String), // 元组变体
       Struct { x: f64 }   // 结构体变体
   }Code language: JavaScript (javascript)
  1. use 作用域:只在当前模块/代码块生效;
  2. 链表案例是 Rust 官方经典入门示例,帮助理解:递归类型、Box、所有权、借用、模式匹配;

Previous:
Next:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注