• 《Rust权威指南》读书笔记6 - Enums and Pattern Matching


      枚举类型,允许我们列举所有可能的值以表示这个类型。枚举可以连同数据以编码信息Rust中的枚举类似于F# Haskell等函数式编程语言中的代数数据类型。

    Defining an Enum 定义枚举

    例如,处理IP地址编码问题,需要针对两种标准进行讨论。一个地址只有两种版本选择,而这两种版本并不兼容。但是不同版本同属于IP地址协议,应当视为同种类型。此时,适合使用枚举类型定义IP地址。

    enum IpAddr {
    	V4,
    	V6,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 其中,V4 V6为enum的两个变体(variant)

    枚举值

    使用::来创建对应枚举值的实例

    let ipv4 = IpAddr::V4;
    
    • 1

    枚举值也可以用在结构体的字段中,声明类型为枚举名,其余与普通变量相同

    关联数据

    枚举允许将与变体关联的数据嵌入枚举变体内,增强枚举变体与其关联数据的耦合。这些关联数据也可以是结构体(标准库就采用这种实现方式)。

    pub enum IpAddr {
        /// An IPv4 address.
        #[stable(feature = "ip_addr", since = "1.7.0")]
        V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr),
        /// An IPv6 address.
        #[stable(feature = "ip_addr", since = "1.7.0")]
        V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 标准库实现IpAddr的具体内容,其中Ipv4AddrIpv6Addr是两个另外定义的结构体

    另一个实例中给出了更加灵活的使用方法

    enum Message {
    	Quit,
    	Move {x: i32, y: i32},
    	Write(String),
    	ChangeColor(i32, i32, i32),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其中,

    • Quit没有关联数据,表示直接退出
    • Move包含一个匿名结构体,表示移动的Offset
    • Wirte包含一个String,用于写入字符串
    • ChangeColor包含了3个i32值,表示改变的目标颜色

    通过以上方式,我们定义了一个统一的消息类型,进而可以设计一个统一的消息处理函数,而不需要多个接口。

    Option 枚举类型

      Option枚举定义于标准库中,描述了值可能不存在的情形,广泛适用于空值处理。空值(Null)本身是一个值,含义是没有值。在设计有空值的语言中,一个变量往往处于两种状态,空值和非空值。

    Rust使用Option枚举来提供类似空值概念,可以用它来标识一个值无效或缺失。

    pub enum Option<T> {
        /// No value.
        #[lang = "None"]
        #[stable(feature = "rust1", since = "1.0.0")]
        None,
        /// Some value of type `T`.
        #[lang = "Some"]
        #[stable(feature = "rust1", since = "1.0.0")]
        Some(#[stable(feature = "rust1", since = "1.0.0")] T),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 该枚举被包含在与导入模块中,不需要显式引入

    • 表示一个泛型参数,将在后面提到。简单说,它代表任何一种类型

    • 当初始化为some时,编译器可自行完成类型推导,当初始化为none时,必须手动指定类型T

    • 这个枚举类型使编程人员必须考虑值为空的情况(没有设计该枚举与对应类型直接相加等操作的情况)

    • 只要一个值不是Option的,那么它就一定是非空的

    • 关于Option枚举的具体使用示例,参见Option in std::option - Rust (rustwiki.org)

    match 控制流运算符

    之前已经用过,match运算符可将一个值与一系列模式进行比较,并根据匹配模式执行代码。模式可以由字面量,变量名,通配符和其他组成。当然,也可以用枚举类型进行模式匹配(这是常见用法)。

    let mode = 'q';
    match mode {
        'w' => todo!(),
        'r' => todo!(),
        'q' => return,
        _ => {println!("Input error!");},
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 枚举的所有分支(arm)必须返回相同类型的数据,并与使用该表达式的变量相匹配
    • 分支采用自上而下的匹配方式,并需要包含所有可能路径(穷尽(exhausitive)匹配),没有任何动作的路径可以用空代码块{}代替。

    使用match匹配 Option

    fn main() {
    	assert_eq!(Some(6), plus_one(Some(5)));
    	assert_eq!(None, plus_one(None));
    	println!("Bingo!")
    }
    
    fn plus_one(x: Option<i32>) -> Option<i32> {
    	match x {
    		None => None,
    		Some(i) => Some(i + 1),
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • Attention: 使用 match guard以强化条件匹配能力
    let num = Some(4);
    
    match num {
        Some(x) if x % 2 == 0 => println!("The number {} is even", x),
        Some(x) => println!("The number {} is odd", x),
        None => (),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 使用_通配符以匹配所有剩余情况,类似Default,在分支内部参数中同样适用
    • 使用|表示多分支合并的关系

    if let 简洁控制流

    使用if let语句可以较简单地实现只关心一种匹配而忽略其他匹配的情况。以下两种表述方式在功能上相同:

    let option_num = Some(3);
    
    // method 1
    match option_num {
        Some(value) => println!("The value is {value}"),
        None => (),
    }
    
    // method 2
    if let Some(value) = option_num {
        println!("The value is {value}");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    if let语句也可使用else,可以看作是match结构的语法糖

  • 相关阅读:
    【scikit-learn基础】--『监督学习』之 空间聚类
    Redis主从模式(一)----搭建主从结构并演示
    完美解决回车事件使用window.open()打开新窗口行为被浏览器拦截的问题【亲测有效】
    8 个有效的安卓数据恢复软件——可让丢失的文件起死回生!
    javaweb登录注册页面页面的完整代码
    编写一个kubernetes controller
    Redis缓存穿透问题的解决思路
    Java架构师常见基础面试题(附答案)
    ES实现三表关联查询+条件过滤
    西安邮电大学第三届网络安全技能大赛---PWN方向WP
  • 原文地址:https://blog.csdn.net/Zheng__Huang/article/details/127612547