一、定义枚举
1.使用enum关键字定义枚举。
语法格式如下
enum enum_name {
variant1,
variant2,
variant3
}
例如
enum Fruits {
Banana, // 香蕉
Pear, // 梨
Mandarin, // 橘子
Eggplant // 茄子
}
2.可以为枚举成员添加属性
enum Book {
Papery(u32),
Electronic(String),
}
let book = Book::Papery(1001);
let ebook = Book::Electronic(String::from("url://..."));
如果你想为属性命名,可以用结构体语法:
enum Book {
Papery { index: u32 },
Electronic { url: String },
}
let book = Book::Papery{index: 1001};
二、使用枚举
(一)使用::
语法格式如下
enum_name::variant
例如
let selected = Fruits::Banana;
let selected: Fruits = Fruits::Banana;
范例
#[derive(Debug)]
enum Fruits {
Banana, // 香蕉
Pear, // 梨
Mandarin, // 橘子
Eggplant // 茄子
}
fn main() {
let selected = Fruits::Banana;
println!("{:?}",selected);
}
编译运行结果如下
Banana
(二)判断枚举
只能使用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);
}
编译运行结果如下
Large sized Sports Utility car
Small sized car
medium sized car
带属性的枚举
#[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);
}
}
}
编译运行结果如下
Name("Mohtashim")
Usr_ID(100)
Mohtashim
三、标准库枚举
标准库内置了很多枚举
(一)Option
这个枚举是Option<T>
,而且它定义于标准库中,如下:
enum Option<T> {
Some(T),
None,
}
它有两个枚举值 None 和 Some(T)。
None 表示可有可无中的无。
Some(T) 表示可有可无中的有,既然有,那么就一定有值,那个T就表示值的类型。
1.定义枚举值
Option<T>
枚举包含在了prelude之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以直接使用Some和None,不需要加Option:: 前缀。
定义枚举值,可以使用Some(value)或者None。
例子:
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
如果使用None,需要指定Option<T>
是什么类型的,因为编译器只通过None无法推断出Some成员的类型。
当有一个Some值时,我们就知道存在一个值,而这个值保存在Some中。当有个None值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。
那么,Option<T>
为什么就比空值要好呢?
当在Rust中拥有一个像i8这样类型的值时,编译器确保它总是有一个有效的值。我们可以自信使用而无需做空值检查。只有当使用Option<i8>
的时候需要担心可能没有值,而编译器会确保我们在使用值之前处理了为空的情况。
换句话说,在对Option<T>
进行T的运算之前必须将其转换为T。通常这能帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况。
不再担心会错误地假设一个非空值,会让你对代码更加有信心。为了拥有一个可能为空的值,你必须要显式地将其放入对应类型的Option<T>
中。接着,当使用这个值时,必须明确地处理值为空的情况。只要一个值不是Option<T>
类型,你就 可以 安全地认定它的值不为空。这是Rust的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加Rust代码的安全性。
2.使用枚举
(1)获取some的值
expect
如果是Some,就返回Some的值。如果是None就打印消息,并崩溃。
Panics
如果值为 None 并带有由 msg 提供的自定义panic消息,就会出现panics。
例子
let x = Some("value");
assert_eq!(x.expect("fruits are healthy"), "value");
let x: Option<&str> = None;
x.expect("fruits are healthy"); // `fruits are healthy` 的panics
unwrap
如果是Some,就返回Some的值。如果是None就崩溃。
由于此函数可能会panic,因此通常不建议使用该函数。 应该使用match显式处理None,或者调用unwrap_or,unwrap_or_else,unwrap_or_default。
Panics
如果self的值等于 None,就会出现panics。
例子
let x = Some("air");
assert_eq!(x.unwrap(), "air");
let x: Option<&str> = None;
assert_eq!(x.unwrap(), "air"); //崩溃
unwrap与expect一样,唯一不同是expect能打印自定义消息,而unwrap不能
(2)判断是否有值
is_some
如果选项是 Some 值,则返回 true。
例子
let x: Option<u32> = Some(2);
assert_eq!(x.is_some(), true);
let x: Option<u32> = None;
assert_eq!(x.is_some(), false);
is_none
如果选项是 None 值,则返回 true。
例子
let x: Option<u32> = Some(2);
assert_eq!(x.is_none(), false);
let x: Option<u32> = None;
assert_eq!(x.is_none(), true);
(3)逻辑运算
and
如果自己为 None,则返回 None; 否则,返回参数。
例子
let x = Some(2);
let y: Option<&str> = None;
assert_eq!(x.and(y), None);
let x: Option<u32> = None;
let y = Some("foo");
assert_eq!(x.and(y), None);
let x = Some(2);
let y = Some("foo");
assert_eq!(x.and(y), Some("foo"));
let x: Option<u32> = None;
let y: Option<&str> = None;
assert_eq!(x.and(y), None);
or
如果自己有值,则返回自己,否则返回参数。
例子
let x = Some(2);
let y = None;
assert_eq!(x.or(y), Some(2));
let x = None;
let y = Some(100);
assert_eq!(x.or(y), Some(100));
let x = Some(2);
let y = Some(100);
assert_eq!(x.or(y), Some(2));
let x: Option<u32> = None;
let y = None;
assert_eq!(x.or(y), None);
xor
如果自己和参数之一恰好是Some,则返回 Some,否则返回 None。
例子
let x = Some(2);
let y: Option<u32> = None;
assert_eq!(x.xor(y), Some(2));
let x: Option<u32> = None;
let y = Some(2);
assert_eq!(x.xor(y), Some(2));
let x = Some(2);
let y = Some(2);
assert_eq!(x.xor(y), None);
let x: Option<u32> = None;
let y: Option<u32> = None;
assert_eq!(x.xor(y), None);
(二)Result
Option枚举返回 None 可以表明失败。但是有时要强调为什么会失败。为做到这点,我们提供了 Result 枚举类型。
enum Result<T, E> {
Ok(T),
Err(E),
}
Result<T, E> 有两个取值:
Ok(value) 表示操作成功,并包装操作返回的 value(value 拥有 T 类型)。
Err(why),表示操作失败,并包装 why,它能够解释失败的原因(why 拥有 E 类型)。
与Option枚举一样,Result枚举和其成员也被导入到了prelude中,所以就不需要在match分支中的Ok和Err之前加Result::。
1.定义枚举
使用Ok(value)和Err(why)
例子
let ok = Ok(5);
let err = Err("not a string");
2.使用枚举
(1)获取Ok的值
expect
返回包含 self 值的包含的 Ok 值。
由于此函数可能为panic,因此通常不建议使用该函数。 应该使用match显式处理 Err,或者调用 unwrap_or,unwrap_or_else,unwrap_or_default。
Panics
如果值为 Err,就会出现panics,其中panic消息包括传递的消息以及 Err 的内容。
例子
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.expect("expect a num"), 2);
let x: Result<u32, &str> = Err("emergency failure");
x.expect("Testing expect"); // `Testing expect: emergency failure` 的panics
unwrap
返回包含 self 值的包含的 Ok 值。
由于此函数可能为panic,因此通常不建议使用该函数。 应该使用match显式处理Err,或者调用unwrap_or,unwrap_or_else,unwrap_or_default。
Panics
如果该值为 Err,就会出现Panics,并由 Err 的值提供panic消息。
例子
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.unwrap(), 2);
let x: Result<u32, &str> = Err("emergency failure");
x.unwrap(); // `emergency failure` 的panics
unwrap_or_else
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {:?}", error);
})
} else {
panic!("Problem opening the file: {:?}", error);
}
});
}
(2)判断是否是ok
is_ok
如果结果为 Ok,则返回 true。
例子
let x: Result<i32, &str> = Ok(-3);
assert_eq!(x.is_ok(), true);
let x: Result<i32, &str> = Err("Some error message");
assert_eq!(x.is_ok(), false);
is_err
如果结果为 Err,则返回 true。
例子
let x: Result<i32, &str> = Ok(-3);
assert_eq!(x.is_err(), false);
let x: Result<i32, &str> = Err("Some error message");
assert_eq!(x.is_err(), true);
(3)转换错误
ok
从 Result<T, E>
转换为 Option<T>
。
将 self 转换为 Option<T>
,使用 self,并丢弃错误 (如果有)。
Examples
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.ok(), Some(2));
let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.ok(), None);
err
从 Result<T, E>
转换为 Option<E>
。
将 self 转换为 Option<E>
,使用 self,并丢弃成功值 (如果有)。
Examples
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.err(), None);
let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.err(), Some("Nothing here"));
(4)函数返回错误,称为传播错误
例子
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
这种传播错误是如此的常见,以至于Rust提供了 ?运算符来简化操作。
?运算符用在返回值为Result的表达式后面,它等同于这样一个match表达式:其中Err(err)分支展开成return Err(err),而Ok(ok)分支展开成ok。
例子
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
可以在 ? 之后直接使用链式方法调用来进一步缩短代码
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
? 运算符只能用于返回Result的函数
下面代码编译错误
use std::fs::File;
fn main() {
let f = File::open("hello.txt")?;
}
有两种方法修复这个问题。一是将函数返回值类型修改为Result<T, E>。二是不使用?运算符。
main函数是特殊的,其返回类型是有限制的。main函数的一个有效的返回值是 (),另一个有效的返回值是Result<T, E>,如下所示:
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}