枚举
枚举也被称作 enums
枚举可以给一个类型列举一系列可能的值
1.定义枚举
- 使用
enum关键字定义一个枚举 enum后面定义枚举的名字- 在
{}中定义枚举的成员,这些成员成为变体,变体之间使用逗号,隔开 - 枚举和变体的名字都需要使用大驼峰的形式,不然rust会警告
enum EnumName{
Variant1,
Variant2,
Variant3,
// ...
}比如,人类按颜色区别可以分为黄色、白色和黑色
enum PersonCate {
Yellow,
White,
Black,
}2.枚举值
通过双引号枚举名::变体的方式,来访问枚举的变体
#[derive(Debug)]
enum PersonCate {
Yellow,
White,
Black,
}
let chinese = PersonCate::Yellow;
let american = PersonCate::White;
let african = PersonCate::Black;
println!("{:?}", chinese); // Yellow
println!("{:?}", american); // White
println!("{:?}", african); // Black这里的#[derive(Debug)]和{:?}在会在后面详细说明,现在先简要说明
#[derive(Debug)]特性允许复杂数据类型被打印{:?}是println!宏的一个特殊占位符,可以打印复杂数据类型
我们也可以定义一个函数,把枚举放在函数参数中
#[derive(Debug)]
enum PersonCate {
Yellow,
White,
Black,
}
fn print_person_cate(person: PersonCate) {
println!("{:?}", person)
}
fn main() {
let chinese = PersonCate::Yellow;
let american = PersonCate::White;
let african = PersonCate::Black;
print_person_cate(chinese); // Yellow
print_person_cate(american); // White
print_person_cate(african); // Black
}3.类C枚举
Rust还允许给枚举体的每个变体设置一个整形数值:
#[repr(u8)]
enum HttpStatus {
Ok = 200,
NotFound = 404,
}此处定义了一个Http状态码的枚举,并且给不同状态设定了对于的数值。顶部的#[repr(u8)]用于指定变体的数据类型,此处指定为u8。
这种枚举本身在Rust中意义不大,因为Rust很少用到整形与枚举直接转换的性质。这个性质主要用于和C语言的接口进行交互,从而兼容C风格的枚举。
4.变体的类型
我们可以给枚举的变体定义一个类型,语法如下
enum EnumName{
Variant1(Type),
// ...
}变体的类型可以是任意类型,比如u8、String、元组、甚至是结构体、也可以是另一个枚举,
#[derive(Debug)]
enum QuitCate {
Walt,
Run,
}
#[derive(Debug)]
struct Student {
name: String,
age: u8,
}
#[derive(Debug)]
enum Message {
Info, // 普通类型的变体
Num(u32), // 类型为u32的变体
Move { x: i32, y: i32 }, // 类型为一个匿名结构体的变体
Quit(QuitCate), // 类型为另一个枚举的变体
Write(String), // 类型为String的变体
Color(i32, i32, i32, i32), // 类型为4个数据的元组的变体
Child(Student) // 类型为一个结构体的变体
}注意!:如果一个枚举实现了#[derive(Debug)]特性,那么这个枚举里使用到的复合类型变体也要实现#[derive(Debug)]特性
而想要创建这些变体的实例
fn main() {
let num=Message::Num(666);
let mov=Message::Move { x: 11, y: 22 };
let quit=Message::Quit(QuitCate::Run);
let write=Message::Write("我服了rust怎么这么难啊".to_string());
let color=Message::Color(255, 255, 255, 255);
let child=Message::Child(Student { name: String::from("ikun"), age: 18 });
println!("{:?}",num); // Num(666)
println!("{:?}",mov); // Move { x: 11, y: 22 }
println!("{:?}",quit); // Quit(Run)
println!("{:?}",write); // Write("我服了rust怎么这么难啊")
println!("{:?}",color); // Color(255, 255, 255, 255)
println!("{:?}",child); // Child(Student { name: "ikun", age: 18 })
}5.枚举方法
和struct一样,枚举也可以通过impl块定义方法
impl Message {
fn call(&self) {
match self {
Message::Write(text) => println!("Text message: {}", text),
_ => println!("Other type of message"),
}
}
}
fn main() {
let m = Message::Write(String::from("hello"));
m.call(); // Text message: hello
}6.模式匹配
使用match对enum进行模式匹配,匹配到相应的变体,可以进行一些操作
enum PersonCate {
Yellow,
White,
Black,
}
fn main() {
let person = PersonCate::Yellow;
let mut is_yellow: bool = false;
match person {
PersonCate::Black => println!("you are black"),
PersonCate::White => println!("you are white"),
PersonCate::Yellow => {
is_yellow = true;
}
}
println!("{}", is_yellow); // true
}7.Option<T>
Option<T>是Rust标准库中一个特殊的枚举,位于std::option::Option,用来表示一个变量可能有值,也可能为空
这像是其他语言中的null
pub enum Option<T> {
None,
Some(T),
}- None:表示没有值,为空
- Some(T):表示有值,值包含在
Some中,类型为T
TIP
Option和其变体Some(T),None均被包含在了std::prelude中,所以在使用时,不需要显示导入
一个简单的使用
fn main() {
let o = Some(123);
let null: Option<i32> = None;
println!("{:?}", o); // Some(123)
println!("{:?}", null); // None
}可以看到,直接使用变量,得到的是Option<T>中的其中一个变体
若想拿到Some中的值,需要使用Option<T>中的方法,比如unwrap
fn main() {
let o = Some(123);
println!("{:?}", o.unwrap()); // 123
}7.1 模式匹配
在开发中经常会用模式匹配来处理Option的值
fn get_value(num: i32) -> Option<i32> {
if num > 10 { Some(num) } else { None }
}
fn main() {
let o = get_value(15);
match o {
Some(value) => println!("{}", value),
None => println!("no value"),
} // 15
let n = get_value(8);
match n {
Some(value) => println!("{}", value),
None => println!("no value"),
} // "no value"
}使用if let语法糖
fn get_value(num: i32) -> Option<i32> {
if num > 10 { Some(num) } else { None }
}
fn main() {
let o = get_value(15);
if let Some(value) = o {
println!("{}", value); // 15
}
let n = get_value(8);
if let None = n {
println!("no value"); // "no value"
}
}8.Result<T, E>
Result<T, E>是Rust标准库中一个特殊的枚举,位于std::result::Result,用来表示一个结果可能有值,也可能为一个错误
通常用在io操作中
enum Result<T, E> {
Ok(T),
Err(E),
}- Ok(T):表示结果是成功的,值包含在
Ok中,值类型为T - Err(E):表示结果是失败的的,错误的信息包含在
Err中,错误的类型为E
TIP
Result和其变体Ok(T),Err(E)均被包含在了std::prelude中,所以在使用时,不需要显示导入
一个简单的使用
fn get_result(num: i32) -> Result<i32, String> {
if num > 10 {
Ok(num)
} else {
Err(String::from("the num is less than 10"))
}
}
fn main() {
let res1 = get_result(8);
let res2 = get_result(50);
println!("{:?}", res1); // Err("the num is less than 10")
println!("{:?}", res2); // Ok(50)
}可以看到,直接使用变量,得到的是Result<T, E>中的其中一个变体
若想拿到Ok中的值,需要使用Result<T, E>中的方法,比如unwrap和unwrap_err
fn get_result(num: i32) -> Result<i32, String> {
if num > 10 {
Ok(num)
} else {
Err(String::from("the num is less than 10"))
}
}
fn main() {
let res1 = get_result(8);
let res2 = get_result(50);
println!("{:?}", res1.unwrap_err()); // "the num is less than 10"
println!("{:?}", res2.unwrap()); // 50
}8.1 模式匹配
在开发中经常会用模式匹配来处理Result的值
fn get_result(num: i32) -> Result<i32, String> {
if num > 10 {
Ok(num)
} else {
Err(String::from("the num is less than 10"))
}
}
fn main() {
let res = get_result(15);
match res {
Ok(value) => println!("{:#?}", value), // 15
Err(err) => println!("{:#?}", err),
}
let res = get_result(5);
match res {
Ok(value) => println!("{:#?}", value),
Err(err) => println!("{:#?}", err), // "the num is less than 10"
}
}使用if let语法糖
fn get_result(num: i32) -> Result<i32, String> {
if num > 10 {
Ok(num)
} else {
Err(String::from("the num is less than 10"))
}
}
fn main() {
let res = get_result(15);
if let Ok(value) = res {
println!("{:#?}", value) // 15
}
let res = get_result(5);
if let Err(err) = res {
println!("{:#?}", err) // "the num is less than 10"
}
}9.?操作符
?操作符是 Rust错误传播的核心语法糖,专门用于处理 Result<T, E>和 Option<T>。
在使用Result和Option时,很多时候需要用match处理错误或者None的结果
为了方便,?操作符可以自动取出Ok/Some中的值,自动返回处理Err/None的结果,而且还不会导致panic
可以理解为?操作符把结果取出来,把错误向上传递
9.1 ?操作符的使用
有如下代码
fn is_more_than_five(num: u8) -> Result<bool, String> {
if num > 5 {
Ok(true)
} else {
Err("小于5".to_string())
}
}
fn main() -> Result<(), String> {
is_more_than_five(6);
Ok(())
}如果is_more_than_five直接调用,编译器会给出如下提示

大概意思是,Result的结果需要被使用,Result可能是Err的变体,需要处理Err这个错误,那么就有如下情形
1.如果直接把结果赋值给一个变量,因为函数返回的是Result,所以想要取出Ok中的值,需要使用unwrap()等函数,这样又会增加panic的风险
let res = is_more_than_five(6).unwrap();2.我们可能就有用match、if let等去处理这个错误
match is_more_than_five(6) {
Ok(num) => println!("{:#?}", num),
Err(err) => println!("{:#?}", err),
}
// 或者使用if let 简化
if let Ok(num) = is_more_than_five(6) {
println!("{:#?}", num)
}但是这样都比较麻烦,尽管if let已经简化了些代码
我们往往都只是想要这个Ok/Some的结果,于是使用?操作符我们可以得到
fn main() -> Result<(), String> {
let res = is_more_than_five(6)?;
println!("{:?}", res); // true
let res = is_more_than_five(3)?;
println!("{:?}", res); // true
Ok(())
}这便是我们想要的结果,简化了代码,同时也避免了panic
9.2 ?操作符的实质
对于Result,差不多相当于这样
- 若结果是
Ok,则直接返回Ok里的值 - 若结果是
Err,就返回Err,并且内部使用From::from包装错误信息
match result {
Ok(v) => v,
Err(e) => return Err(From::from(e)),
}对于Option,差不多相当于这样
- 若结果是
Some,则直接返回Some里的值 - 若结果是
None,就直接返回None
match option {
Some(v) => v,
None => return None,
};9.3 错误类型必须兼容
在一个返回Result的函数中,调用另一个返回Result的函数
想要使用?操作符,两个函数中的Result<T,E>中E的类型必须一致
上面的代码,两个函数返回值错误类型都是String,所以没有问题
fn is_more_than_five(num: u8) -> Result<bool, String> {
if num > 5 {
Ok(true)
} else {
Err("小于5".to_string())
}
}
fn main() -> Result<(), String> {
let res = is_more_than_five(6)?;
println!("{:?}", res); // true
let res = is_more_than_five(3)?;
println!("{:?}", res); // true
Ok(())
}如果是这样
fn is_more_than_five(num: u8) -> Result<bool, String> {
if num > 5 {
Ok(true)
} else {
Err("小于5".to_string())
}
}
fn main() -> Result<(), std::io::Error> {
let res = is_more_than_five(6)?;
println!("{:?}", res); // true
let res = is_more_than_five(3)?;
println!("{:?}", res); // true
Ok(())
}那么编译器就会提示报错了

开发中难免会遇到这种情况,那么该怎么办呢
使用map_err()
使用Result自带的map_err方法把错误转化为一样的类型,这是比较常用的
fn is_more_than_five(num: u8) -> Result<bool, String> {
if num > 5 {
Ok(true)
} else {
Err("小于5".to_string())
}
}
use std::io;
fn main() -> Result<(), io::Error> {
let res = is_more_than_five(6)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
println!("{:?}", res); // true
Ok(())
}使用impl From
fn inner() -> Result<i32, io::Error> {
// ...
}
fn outer() -> Result<i32, MyError> {
let v = inner()?; // ❌ 编译错误
Ok(v)
}给自己的错误类型实现 From
use std::io;
use std::num::ParseIntError;
#[derive(Debug)]
enum MyError {
Io(io::Error),
Parse(ParseIntError),
}
impl From<io::Error> for MyError {
fn from(err: io::Error) -> Self {
MyError::Io(err)
}
}
impl From<ParseIntError> for MyError {
fn from(err: ParseIntError) -> Self {
MyError::Parse(err)
}
}再使用
fn outer() -> Result<i32, MyError> {
let v = inner()?; // ✅ io::Error → MyError::Io
Ok(v)
}