写点什么

5 分钟速读之 Rust 权威指南(九)

用户头像
码生笔谈
关注
发布于: 2021 年 05 月 25 日
5分钟速读之Rust权威指南(九)

枚举

枚举类型,通常也被简称为枚举,它允许我们列举所有可能的值来定义一个类型,对于用过 TS 的前端同学来说会容易理解一些,不过 rust 中的枚举类型更强大。

定义枚举

定义一个 IP 地址的枚举类型,目前有两种被广泛使用的 IP 地址标准:IPv4 和 IPv6,两种 IP 类型被称作变体:


#[derive(Debug)] // 加了Debug trait才能打印每一个枚举值enum IpAddrKind {  V4,  V6,}
let four = IpAddrKind::V4;let six = IpAddrKind::V6;println!("{:?}", four); // V4println!("{:?}", six); // V6
复制代码

使用枚举

枚举值可以用来标识函数参数的类型:


// 标识参数类型fn route(ip: IpAddrKind) {  println!("{:?}", ip)}
route(IpAddrKind::V4); // V4route(IpAddrKind::V6); // V6
复制代码

在结构体中使用

定义一个结构体,包含 IP 类型和 IP 地址:


struct IpAddr {  ip: IpAddrKind,  address: String,}
// 实现一个IP类型为V4的结构体let home = IpAddr { ip: IpAddrKind::V4, address: String::from("127.0.0.1")};
// 实现一个IP类型为V6的结构体let loopback = IpAddr { ip: IpAddrKind::V6, address: String::from("::1")};
复制代码

枚举关联类型/值

事实上枚举允许我们直接将其关联的数据嵌入枚举变体内。我们可以使用枚举来更简捷地表达出上述概念,而不用将枚举集成至结构体中:


#[derive(Debug)]enum IpAddrKind2 {  V4(String), // 关联一个String类型  V6(String), // 关联一个String类型}
// 创建V4变体的枚举,并关联Stringlet home = IpAddrKind2::V4(String::from("127.0.0.1"));
// 创建V6变体的枚举,并关联Stringlet loopback = IpAddrKind2::V6(String::from("::1"));
println!("{:?}", home); // V4("127.0.0.1")println!("{:?}", loopback); // V6("::1")
复制代码

枚举支持多参数

将 V4 用多个参数来更加详细的描述地址,其实可以将关联的参数整体理解成元组:


#[derive(Debug)]enum IpAddrKind3 {  V4(u8, u8, u8, u8), // 将String改为4个u8  V6(String),}
// 使用时也要传入4个u8类型的参数let home = IpAddrKind3::V4(255, 255, 255, 255);let loopback = IpAddrKind3::V6(String::from("::1"));
println!("{:?}", home); // V4(255, 255, 255, 255)println!("{:?}", loopback); // V6("::1")
复制代码

多类型枚举与结构体

一个枚举中可以包含多个类型的变体:


#[derive(Debug)]enum Message {  // 没有关联任何数据  Quit,  // 包含一个匿名结构体  Move { x: i32, y: i32 },  // 包含一个String  Write (String),  // 包含三个i32(一个元祖)  ChangeColor (u8, u8, u8),}
// 创建一个Move变体let mov = Message::Move { x: 100, y: 100 };println!("{:?}", mov); // Move { x: 100, y: 100 }
复制代码

枚举和结构体的区别

其实上边的多类型枚举的例子中,每一种变体都可以用单独的结构体实现:


// 结构体struct Quit; // 空结构体struct Move { x: i32, y: i32 }struct Write(String); // 元组结构体struct ChangeColor(u8, u8, u8); // 元组结构体
复制代码


两种实现方式之间的差别在于,假如我们使用了不同的结构体,那么每个结构体都会拥有自己的类型,我们无法定义一个能够统一处理这些类型数据的函数,而枚举则不同,因为它是单独的一个类型,只要在函数中标注参数类型为枚举即可:


// 使用枚举来标识参数类型fn poster(msg: Message) {}
// 使用结构体的话,需要四个不同的函数fn quit_poster(msg: Quit) {}fn move_poster(msg: Move) {}fn write_poster(msg: Write) {}fn change_color_poster(msg: ChangeColor) {}
复制代码

为枚举实现方法

枚举也可以像结构体一样实现方法:


impl Message {  fn call(&self) -> &Message {    return self;  }}let msg = Message::Move { x: 100, y: 100 };msg.call(); // Move { x: 100, y: 100 }
复制代码

Option 枚举

rust 中没有空值,但却提供了一个拥有类似概念的枚举,我们可以用它来标识一个值无效或缺失。这个枚举就是 Option<T>:


enum Option<T> { // 这个枚举中包含一个泛型T,后边章节会介绍泛型  Some(T),  None,}
复制代码


由于 Option 非常常用,rust 已经做了优化,我们可以在不加 Option::前缀的情况下直接使用 Some 或 None:


// rust会自动推断类型let some_string = Option::Some("some");
// 也可以显式标注类型let some_string: Option<&str> = Option::Some("some");
// 可以省略Option::let some_string: Option<&str> = Some("some");
// None必须显式给出类型声明,编译器无法推断出类型let absent_string: Option<i32> = Option::None;
// 省略Option::let absent_string: Option<i32> = None;
println!("{:?}", some_string); // Some("some")println!("{:?}", absent_string); // None
复制代码


使用 Option 类型时,其中包含的值不能直接使用:


let x: i32 = 10;let o:Option<i32> = Some(5);println!("{}", x + o) // 报错,i32类型不能与Option相加
复制代码


如果要进行运算,必须要将 Option<T>中的 T 取出来,例如使用 if let 表达式:


if let Some(v) = o {  println!("{}", x + v); // 15}
复制代码


发布于: 2021 年 05 月 25 日阅读数: 7
用户头像

码生笔谈

关注

欢迎关注「码生笔谈」公众号 2018.09.09 加入

前端、Rust干货分享,一起成为更好的Javascripter & Rustacean

评论

发布
暂无评论
5分钟速读之Rust权威指南(九)