Option
Option 表示一个值可能存在, 也可能不存在, 它避免了直接使用 null/空指针 带来的问题, 强制要求程序员处理"值不存在"这种情况
rust
enum Option<T> {
Some(T), // 值存在, 里面装着值
None, // 值不存在
}常见的返回 Option 的场景:
- 从集合(HashMap/Vec)中查找元素
- 字符串解析(parse 失败)
- 可能为空的字段(比如用户的可选头像, 数据库可能存的是 NULL 类型)
创建 Option
| 方式 | 说明 |
|---|---|
Some(value) | 值存在 |
None | 值不存在 |
| Result::ok | 将 Result 的 Ok 变成 Some, Err 变成 None |
rust
fn main() {
// 1. 直接创建
let some_num: Option<i32> = Some(10);
let none_num: Option<i32> = None;
println!("{some_num:?}"); // Some(10)
println!("{none_num:?}"); // none
// 2. 常见的使用场景: 字符串解析
// 解析成功, ok() 将 Result 转换为 Option
let num: Option<i32> = "42".parse().ok();
println!("{num:?}"); // Some(42)
// 解析失败, 返回 None
let num: Option<i32> = "abc".parse().ok();
println!("{num:?}"); // None
// 3. 常见的使用场景: 从集合中查找元素
let mut map = std::collections::HashMap::new();
map.insert("name", "张三");
// 键存在, 返回 Some(值)
let name = map.get("name");
println!("{name:?}"); // Some("张三")
// 键不存在, 返回 None
let name = map.get("age");
println!("{name:?}"); // None
}判断 Option 是否有值
| 方法 | 作用 |
|---|---|
| is_some | 是否有值(Some) |
| is_none | 是否没有值(None) |
| is_some_and | 是否有值, 并且值满足条件 |
| is_none_or | 如果option为none返回true, 否则返回闭包函数返回的值 |
rust
fn main() {
let some_num: Option<i32> = Some(10);
let none_num: Option<i32> = None;
println!("some_num.is_some() = {}", some_num.is_some()); // true
println!("some_num.is_none() = {}", some_num.is_none()); // false
println!("none_num.is_some() = {}", none_num.is_some()); // false
println!("none_num.is_none() = {}", none_num.is_none()); // true
// is_some_and: 有值 并且 值满足条件才返回 true
println!("{}", some_num.is_some_and(|x| x > 5)); // true
println!("{}", some_num.is_some_and(|x| x > 20)); // false
// is_none_or: 如果 option 的值是 none 直接返回 true
// 否则看闭包函数返回的 bool 值是什么就返回什么
println!("{}", some_num.is_none_or(|_| false)); // false: 闭包返回了 false
println!("{}", some_num.is_none_or(|_| true)); // true: 闭包返回了 true
println!("{}", none_num.is_none_or(|_| false)); // true: 直接返回true, 因为 none_num 是 none
println!("{}", none_num.is_none_or(|_| true)); // true: 所以 is_none_or 中的闭包无论返回什么都无所谓
// 最常用的判断方式: if let
// 只有值为 Some 时才会进入分支, 并且自动取出里面的值
if let Some(x) = some_num {
println!("x = {x}"); // x = 10
}
// is_some_and 也可以用 if let 来代替
if let Some(x) = some_num
&& x > 5
{
println!("x > 5");
}
}解包取值
| 方法 | 作用 | 注意点 |
|---|---|---|
| unwrap | 取出 Some 中的值 | 如果是 None 会直接 panic |
| expect | 取出 Some 中的值, 可以自定义 panic 消息 | 如果是 None 会直接 panic |
| unwrap_or | 取出 Some 中的值, None 时返回传入的默认值 | 默认值是直接传入的(立即求值) |
| unwrap_or_else | 取出 Some 中的值, None 时执行传入的闭包 | 默认值是惰性求值的(闭包) |
| unwrap_or_default | 取出 Some 中的值, None 时返回类型的默认值 | 需要类型实现了 Default trait |
rust
fn main() {
let some_num: Option<i32> = Some(10);
let none_num: Option<i32> = None;
// unwrap: 取出 Some 中的值
let x = some_num.unwrap();
println!("x = {x}"); // x = 10
// 如果值是 None, unwrap 会直接 panic
// let x = none_num.unwrap();
// thread 'main' panicked at src/main.rs:8:23:
// called `Option::unwrap()` on a `None` value
// expect: 与 unwrap 相同, 但是 panic 消息可以自定义
// let x = none_num.expect("none_num 不应该是 None");
// thread 'main' panicked at src/main.rs:14:23:
// none_num 不应该是 None
// unwrap_or: None 时返回传入的默认值
let x = none_num.unwrap_or(0);
println!("x = {x}"); // x = 0
// unwrap_or_else: None 时执行闭包, 返回闭包的结果
let x = none_num.unwrap_or_else(|| {
println!("none_num 是 None, 执行闭包");
0
});
println!("x = {x}"); // x = 0
// unwrap_or_default: None 时返回该类型的默认值(0)
let x = none_num.unwrap_or_default();
println!("x = {x}"); // x = 0
}注意点
unwrap 和 expect 遇到 None 会直接 panic, 一般只建议在测试代码中使用, 或者你明确代码执行逻辑就应该 panic, 那么可以使用 expect, 或 或者确定值一定存在的情况下使用 unwrap, 正常业务代码推荐使用 unwrap_or 系列方法
转换 Option 的值
| 方法 | 作用 |
|---|---|
| map | 将 Some 中的值通过闭包转换(还是返回一个 Option 值), None 不做处理 |
| map_or | 相当于 map(...).unwrap_or(默认值), 直接返回 Some 中的值 |
| map_or_else | 与 map_or 相同, 但是默认值也是闭包(惰性求值) |
| and_then | 与 map 类似, 但是闭包返回的是 Option, 常用于链式调用(相当于 flatMap) |
| filter | 值不满足条件时变为 None |
| flatten | 将嵌套的 Option 展开一层 |
rust
fn main() {
let some_num: Option<i32> = Some(10);
let none_num: Option<i32> = None;
// map: Some(10) 经过闭包转换 -> Some(20)
let x = some_num.map(|n| n * 2);
println!("{x:?}"); // Some(20)
// None 不做任何处理, 直接返回 None
let x = none_num.map(|n| n * 2);
println!("{x:?}"); // None
// map_or: 相当于 map(...).unwrap_or(默认值)
let x = some_num.map_or(0, |n| n * 2);
println!("x = {x}"); // x = 20
let x = none_num.map_or(0, |n| n * 2);
println!("x = {x}"); // x = 0
// map_or_else: 默认值也由闭包提供
let x = some_num.map_or_else(|| 0, |n| n * 2);
println!("x = {x}"); // x = 20, some_num 是 Some, 所以执行闭包
let x = none_num.map_or_else(|| 0, |n| n * 2);
println!("x = {x}"); // x = 0, 因为 none_num 是 None, 所以取默认值
// and_then:
// 1.调用 .and_then 的 Option 值必须是 Some 否则结果为 None
// 2.and_then 闭包函数中必须返回一个 Some 否则结果为 None
let x = Some("42").and_then(|s| s.parse::<i32>().ok());
println!("{x:?}"); // Some(42)
let x = Some("abc").and_then(|s| s.parse::<i32>().ok());
println!("{x:?}"); // None
let x = None.and_then(|s: &str| s.parse::<i32>().ok());
println!("{x:?}"); // None
// filter: 条件不满足时变成 None
let x = some_num.filter(|n| *n > 5);
println!("{x:?}"); // Some(10)
let x = some_num.filter(|n| *n > 20);
println!("{x:?}"); // None
// flatten: 展开一层嵌套
let x: Option<Option<i32>> = Some(Some(10));
println!("{x:?}"); // Some(Some(10))
println!("{:?}", x.flatten()); // Some(10)
}组合多个 Option
| 方法 | 作用 |
|---|---|
| and | 两个都是 Some, 返回第二个; 有一个是 None 就返回 None |
| or | 第一个是 Some 返回第一个, 否则返回第二个 |
| xor | 只有一个 Some 时返回那个 Some; 两个都是 Some 返回 None |
| or_else | 与 or 相同, 但是第二个是闭包(惰性求值) |
| zip | 将两个 Option 压缩为 一个 Option<(T, U)> 值为元组 |
| unzip | 将一个 Option<(T, U)> 值为元组解压缩为 Option<T> 和 Option<U> |
rust
fn main() {
let some1: Option<i32> = Some(1);
let some2: Option<i32> = Some(2);
let none: Option<i32> = None;
// and: 两个都是 Some 返回第二个
println!("{:?}", some1.and(some2)); // Some(2)
println!("{:?}", some1.and(none)); // None
println!("{:?}", none.and(some2)); // None
// or: 第一个是 Some 就返回第一个
println!("{:?}", some1.or(some2)); // Some(1)
println!("{:?}", none.or(some2)); // Some(2)
// xor: 两个都是 Some 返回 None, 只有一个 Some 返回那个 Some
println!("{:?}", some1.xor(some2)); // None
println!("{:?}", none.xor(some2)); // Some(2)
// or_else: 第二个参数是闭包
println!("{:?}", none.or_else(|| Some(3))); // Some(3)
// zip: 压缩两个 Some 值的 Option 为一个 Option<(x, y)>
// 如果其中有一个是 None 则结果为 None
println!("{:?}", some1.zip(some2)); // Some((1, 2))
println!("{:?}", some1.zip(none)); // None
// unzip: 将一个 Some 值为元组的 Option 解压缩为两个 Option
let zip_opt = Some((1, "hello"));
let (x, y) = zip_opt.unzip();
println!("x={x:?}, y={y:?}"); // x=Some(1), y=Some("hello")
}修改与填充 Option 的值
| 方法 | 作用 |
|---|---|
| take | 取出值, 并将自身替换为 None |
| replace | 取出旧值, 并将自身替换为新值 |
| insert | |
| get_or_insert | 没有值时先插入默认值, 再返回值的引用 |
| get_or_insert_with | 与 get_or_insert 效果一样, 但参数是闭包(惰性求值) |
rust
fn main() {
// take: 取出值, 自身变成 None
// 注: 这个函数会修改自身的值, 所以需要变量必须是可变的
let mut opt = Some(10);
let val = opt.take();
println!("val = {val:?}, opt = {opt:?}"); // val = Some(10), opt = None
// replace: 取出旧值, 自身变成新值
// 注: 这个函数会修改自身的值, 所以需要变量必须是可变的
let mut opt = Some(10);
let val = opt.replace(20);
println!("val = {val:?}, opt = {opt:?}"); // val = Some(10), opt = Some(20)
// insert: 将值插入到 Option 中, 返回一个可变引用
let mut opt = None;
let mut_ref = opt.insert(22); // &mut i32 -> 22
println!("mut_ref = {mut_ref:?}"); // 22
*mut_ref = 33;
println!("mut_ref = {mut_ref:?}"); // 33
// get_or_insert: 没有值就先插入默认值, 再返回值得可变引用
// 如果有值, 就直接返回值得可变引用
let mut opt = None;
let mut_ref = opt.get_or_insert(100);
println!("mut_ref = {mut_ref}"); // 100
// println!("opt = {opt:?} "); // Some(100)
*mut_ref = 111; // 修改可变引用的值
println!("mut_ref = {mut_ref}"); // 111
// get_or_insert_with: 没有值就先插入默认值, 再返回值得可变引用
// 如果有值, 就直接返回值得可变引用
let mut opt = Some(200);
let mut_ref = opt.get_or_insert_with(|| 300);
println!("mut_ref = {mut_ref}"); // 200
// println!("opt = {opt:?} "); // Some(200)
*mut_ref = 222; // 修改可变引用的值
println!("mut_ref = {mut_ref}"); // 222
}Option 与 Result 的互相转换
| 方法 | 作用 |
|---|---|
| ok_or | Option 转 Result, None 时返回传入的错误值 |
| ok_or_else | 与 ok_or 相同, 错误值由闭包提供(惰性求值) |
| transpose | 将 Option<Result<T, E>> 转为 Result<Option<T>, E> |
| Result::ok | Result 转 Option, Ok -> Some, Err -> None |
| Result::err | Result 转 Option, Err -> Some, Ok -> None |
rust
fn main() {
let some_num: Option<i32> = Some(10);
let none_num: Option<i32> = None;
// ok_or: Option 转 Result, None 时返回传入的错误值
let r: Result<i32, String> = some_num.ok_or("没有值".to_string());
println!("{r:?}"); // Ok(10)
let r: Result<i32, String> = none_num.ok_or("没有值".to_string());
println!("{r:?}"); // Err("没有值")
// ok_or_else: 错误值由闭包提供
let r: Result<i32, String> = none_num.ok_or_else(|| "没有值".to_string());
println!("{r:?}"); // Err("没有值")
// Result::ok: Result 转 Option, 只关心成功的情况
let r: Result<i32, String> = Ok(10);
println!("{:?}", r.ok()); // Some(10)
let r: Result<i32, String> = Err("出错了".to_string());
println!("{:?}", r.ok()); // None
// Result::err: Result 转 Option, 只关心失败的情况
let r: Result<i32, String> = Err("出错了".to_string());
println!("{:?}", r.err()); // Some("出错了")
}引用与迭代适配
| 方法 | 作用 |
|---|---|
| as_ref | 将 &Option<T> 转为 Option<&T> |
| as_deref | |
| as_mut | |
| as_deref_mut | |
| iter | |
| iter_mut | |
| cloned | |
| copied |
rust
fn main() {
// 1.message 的类型是: Option<String>
let msg_opt = Some(String::from("abc"));
// 此时调用 map 的是 Option<String>, 闭包中的 s 是 String 类型
// 则会导致 msg_opt 内部值(String)的所有权移动到闭包中
let length = msg_opt.map(|s| s.len());
println!("length = {length:?}");
// println!("msg_opt = {msg_opt:?}");
// ^^^^^^^ value borrowed here after move
// 2.msg_opt 的类型是: Option<String>
// msg_ref 的类型是: Option<&String>
let msg_opt = Some(String::from("abc"));
let msg_ref = msg_opt.as_ref();
// 此时调用 map 的是 Option<&String>, 闭包中的 s 是 &String 类型
// &String 类型并不会获得所有权, 所以 msg_opt 内部值的所有权并没有被移动
let length = msg_ref.map(|s| s.len());
println!("length = {length:?}");
println!("msg_opt = {msg_opt:?}");
}rust
fn main() {
// 1.message 的类型是: Option<String>
let msg_opt = Some(String::from("abc"));
// 此时调用 map 的是 Option<String>, 闭包中的 s 是 String 类型
// 则会导致 msg_opt 内部值(String)的所有权移动到闭包中
let length = msg_opt.map(|s| s.len());
println!("length = {length:?}");
// println!("msg_opt = {msg_opt:?}");
// ^^^^^^^ value borrowed here after move
// 2.msg_opt 的类型是: Option<String>
// msg_ref 的类型是: Option<String> -> Option<&String> -> Option<&str>
let msg_opt = Some(String::from("abc"));
let msg_ref = msg_opt.as_deref();
// 此时调用 map 的是 Option<&str>, 闭包中的 s 是 &str 类型
// &str 类型并不会获得所有权, 所以 msg_opt 内部值的所有权并没有被移动
let length = msg_ref.map(|s| s.len());
println!("length = {length:?}");
println!("msg_opt = {msg_opt:?}");
}rust
fn main() {
// 1.message 的类型是: Option<String>
let mut msg_opt = Some(String::from("abc"));
// 2.msg_mut 的类型是: Option<&mut String>
// 注: 必须是 mut 修饰的变量才能用这个方法
let msg_mut = msg_opt.as_mut();
msg_mut.unwrap().push_str("hello");
println!("msg_opt {msg_opt:?}");
}rust
fn main() {
// 1.message 的类型是: Option<String>
let mut msg_opt = Some(String::from("abc"));
// 2.msg_deref_mut 的类型是: Option<&mut str>
let msg_deref_mut = msg_opt.as_deref_mut();
let res = msg_deref_mut.unwrap().repeat(2);
println!("msg_opt {msg_opt:?}");
println!("res {res:?}");
}rust
fn main() {
// 直接取出 Option 中的值
let msg_opt = Some(String::from("abc"));
for item in msg_opt.iter() {
println!("item = {item:?}");
// abc
}
let vec_opt = Some(vec![1, 2, 3, 4, 5]);
for item in vec_opt.iter() {
println!("item = {item:?}");
// [1,2,3,4,5]
}
// 如果是 None 值, 则会忽略
let non_opt: Option<i32> = None;
for item in non_opt.iter() {
println!("item = {item:?}");
}
}rust
fn main() {
// 取出Some中的值并遍历为可变引用
let mut vec_opt = Some(vec![1, 2, 3, 4, 5]);
for item in vec_opt.iter_mut() {
*item = vec![5, 4, 3, 2, 1];
println!("item = {item:?}");
// [5, 4, 3, 2, 1]
}
// 如果是 None 值, 则会忽略
let non_opt: Option<i32> = None;
for item in non_opt.iter() {
println!("item = {item:?}");
}
}rust
fn main() {
// i32 是 Copy + Clone
let n = 42;
let opt_ref: Option<&i32> = Some(&n);
let copied: Option<i32> = opt_ref.copied();
let cloned: Option<i32> = opt_ref.cloned();
println!("copied = {:?}", copied); // Some(42)
println!("cloned = {:?}", cloned); // Some(42)
// String 是 Clone,但不是 Copy
let s = String::from("hello");
let opt_s_ref: Option<&String> = Some(&s);
let cloned_s: Option<String> = opt_s_ref.cloned();
// let copied_s = opt_s_ref.copied();
// 编译错误: String 类型没有实现 Copy trait
println!("cloned_s = {:?}", cloned_s); // Some("hello")
println!("s 仍然可用: {}", s);
/*
Copy 与 Clone trait 接口的区别与共性:
0. 实现了 Copy 可以使用(copied 和 cloned), 实现了 Clone 可以使用(cloned)
1. Copy 是按位复制, 一般速度非常快
2. Copy 是 Clone 的子接口, 也就是说, 实现了 Copy 的类型会自动实现 Clone 接口
3. Copy 接口只有 "标量类型实现了", 其他复合类型如: String/Vec 等动态大小的类型没有实现
4. 能用 copied 优先用这个函数, 语义更清晰, 速度也快
*/
}在 Option 上使用 ? 运算符
rust
fn main() {
println!("{:?}", find_user(Some(1))); // Some("用户1")
println!("{:?}", find_user(None)); // None
}
// ? 用于 Option 时, 如果值是 None, 直接 return None
// 等价于:
// fn find_user(user_id: Option<u32>) -> Option<String> {
// let id = match user_id {
// Some(id) => id,
// None => return None,
// };
// Some(format!("用户{id}"))
// }
fn find_user(user_id: Option<u32>) -> Option<String> {
let id = user_id?;
Some(format!("用户{id}"))
}注意点
? 运算符只能用在返回类型为 Option 或 Result 的函数中
Result
Result 表示一个操作可能成功, 也可能失败:
Ok(T): 操作成功, 里面装的是返回值Err(E): 操作失败, 里面装的是错误信息
rust
enum Result<T, E> {
Ok(T),
Err(E),
}常见的返回 Result 的场景:
- 文件读写
- 网络请求
- 字符串解析
- 数据库操作
创建 Result
| 方式 | 说明 |
|---|---|
Ok(value) | 操作成功 |
Err(error) | 操作失败 |
| Option::ok_or | 将 Option 转换为 Result |
rust
fn main() {
// 1. 直接创建
let ok: Result<i32, String> = Ok(10);
let err: Result<i32, String> = Err("出错了".to_string());
println!("{ok:?}"); // Ok(10)
println!("{err:?}"); // Err("出错了")
// 2. 通过函数返回
let r = divide(10, 2);
println!("{r:?}"); // Ok(5)
let r = divide(10, 0);
println!("{r:?}"); // Err("除数不能为 0")
}
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
return Err("除数不能为 0".to_string());
}
Ok(a / b)
}取出 Result 的值
| 方法 | 作用 | 注意点 |
|---|---|---|
| unwrap | 取出 Ok 中的值 | Err 会直接 panic |
| expect | 取出 Ok 中的值, 可以自定义 panic 消息 | Err 会直接 panic |
| unwrap_or | 取出 Ok 中的值, Err 时返回传入的默认值 | 默认值是直接传入的(立即求值) |
| unwrap_or_else | 取出 Ok 中的值, Err 时执行传入的闭包(可以拿到错误) | 默认值是惰性求值的(闭包) |
| unwrap_or_default | 取出 Ok 中的值, Err 时返回类型的默认值 | 需要类型实现了 Default trait |
rust
fn main() {
// unwrap: 取出 Ok 中的值
let ok: Result<i32, String> = Ok(10);
let x = ok.unwrap();
println!("x = {x}"); // x = 10
// 如果是 Err, unwrap 会直接 panic
// let err: Result<i32, String> = Err("出错了".to_string());
// let x = err.unwrap();
// thread 'main' panicked at src/main.rs:6:23:
// called `Result::unwrap()` on an `Err` value: "出错了"
// expect: 与 unwrap 相同, 但是 panic 消息可以自定义
// let err: Result<i32, String> = Err("出错了".to_string());
// let x = err.expect("不应该出错");
// unwrap_or: Err 时返回默认值
let err: Result<i32, String> = Err("出错了".to_string());
let x = err.unwrap_or(0);
println!("x = {x}"); // x = 0
// unwrap_or_else: Err 时执行闭包, 并且闭包可以拿到错误信息
let err: Result<i32, String> = Err("出错了".to_string());
let x = err.unwrap_or_else(|e| {
println!("错误信息: {e}"); // 错误信息: 出错了
0
});
println!("x = {x}"); // x = 0
// unwrap_or_default: Err 时返回该类型的默认值(0)
let err: Result<i32, String> = Err("出错了".to_string());
let x = err.unwrap_or_default();
println!("x = {x}"); // x = 0
}注意点
unwrap 和 expect 遇到 Err 会直接 panic, 一般只建议在测试代码中, 或者确定操作一定成功的情况下使用
判断 Result 是成功还是失败
| 方法 | 作用 |
|---|---|
| is_ok | 是否成功(Ok) |
| is_err | 是否失败(Err) |
| is_ok_and | 是否成功, 并且值满足条件 |
rust
fn main() {
let ok: Result<i32, String> = Ok(10);
let err: Result<i32, String> = Err("出错了".to_string());
println!("ok.is_ok() = {}", ok.is_ok()); // true
println!("ok.is_err() = {}", ok.is_err()); // false
println!("err.is_ok() = {}", err.is_ok()); // false
println!("err.is_err() = {}", err.is_err()); // true
// 最常用的处理方式: match
let r = divide(10, 0);
match r {
Ok(x) => println!("结果是 {x}"),
Err(e) => println!("出错了: {e}"), // 出错了: 除数不能为 0
}
}
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
return Err("除数不能为 0".to_string());
}
Ok(a / b)
}转换 Result 的值
| 方法 | 作用 |
|---|---|
| map | 将 Ok 中的值通过闭包转换, Err 不做处理 |
| map_err | 将 Err 中的错误通过闭包转换, Ok 不做处理 |
| map_or | 相当于 map(...).unwrap_or(默认值) |
| and_then | 与 map 类似, 但是闭包返回的是 Result, 常用于链式调用(相当于 flatMap) |
rust
fn main() {
// map: 只转换 Ok 中的值
let x: Result<i32, String> = Ok(10).map(|n| n * 2);
println!("{x:?}"); // Ok(20)
let err: Result<i32, String> = Err("出错了".to_string());
let x = err.map(|n| n * 2);
println!("{x:?}"); // Err("出错了")
// map_err: 只转换 Err 中的错误
let err: Result<i32, String> = Err("出错了".to_string());
let x = err.map_err(|e| format!("错误信息: {e}"));
println!("{x:?}"); // Err("错误信息: 出错了")
// map_or: 相当于 map(...).unwrap_or(默认值)
// 注意: map_or 返回的是转换后的值, 不再是 Result
let ok: Result<i32, String> = Ok(10);
let x: i32 = ok.map_or(0, |n| n * 2);
println!("x = {x}"); // x = 20
let err: Result<i32, String> = Err("出错了".to_string());
let x: i32 = err.map_or(0, |n| n * 2);
println!("x = {x}"); // x = 0
// and_then: 闭包返回的是 Result, 常用于链式调用
let x: Result<i32, String> = Ok(10).and_then(|n| divide(n, 2));
println!("{x:?}"); // Ok(5)
let x: Result<i32, String> = Err("出错了".to_string()).and_then(|n| divide(n, 2));
println!("{x:?}"); // Err("出错了")
}
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
return Err("除数不能为 0".to_string());
}
Ok(a / b)
}组合多个 Result
| 方法 | 作用 |
|---|---|
| and | 两个都是 Ok 返回第二个, 有一个 Err 就返回那个 Err |
| or | 第一个是 Ok 返回第一个, 否则返回第二个 |
| or_else | 与 or 相同, 但是第二个是闭包(惰性求值) |
rust
fn main() {
// and: 两个都是 Ok 返回第二个, 有一个 Err 返回那个 Err
let r: Result<i32, String> = Ok(1).and(Ok(2));
println!("{r:?}"); // Ok(2)
let r: Result<i32, String> = Ok(1).and(Err("出错了".to_string()));
println!("{r:?}"); // Err("出错了")
// or: 第一个是 Ok 就返回第一个
// 注意: or 允许两个 Result 的错误类型不同, 所以需要显式标注类型
let ok1: Result<i32, String> = Ok(1);
let ok2: Result<i32, String> = Ok(2);
let r = ok1.or(ok2);
println!("{r:?}"); // Ok(1)
let err: Result<i32, String> = Err("出错了".to_string());
let ok2: Result<i32, String> = Ok(2);
let r = err.or(ok2);
println!("{r:?}"); // Ok(2)
// or_else: 第二个参数是闭包
let r: Result<i32, String> = Err("出错了".to_string())
.or_else(|e| Err(format!("{e}, 请重试")));
println!("{r:?}"); // Err("出错了, 请重试")
}? 运算符与错误传播
? 是处理 Result 最常用的方式: 如果是 Ok, 自动取出里面的值继续执行; 如果是 Err, 直接将错误 return 给调用方, 也就是所谓的错误传播
rust
fn main() {
let r = parse_and_double("42");
println!("{r:?}"); // Ok(84)
let r = parse_and_double("abc");
println!("{r:?}"); // Err(ParseIntError { kind: InvalidDigit })
}
fn parse_and_double(s: &str) -> Result<i32, std::num::ParseIntError> {
// ? 运算符: 如果返回值是 Err, 直接 return Err 向上传播
// 如果是 Ok, 自动取出里面的值, 继续往下执行
// 等价于:
// let num = match s.parse::<i32>() {
// Ok(num) => num,
// Err(e) => return Err(e),
// };
let num = s.parse::<i32>()?;
Ok(num * 2)
}main 函数本身也可以返回 Result, 出错时程序会打印错误信息并以非 0 状态码退出
rust
use std::fs;
fn main() -> Result<(), std::io::Error> {
let content = fs::read_to_string("hello.txt")?;
println!("{content}");
Ok(())
}当函数中可能产生多种不同类型的错误时, 可以使用 Box<dyn Error> 统一错误类型
rust
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 文件读取产生的 io::Error 和 解析产生的 ParseIntError
// 都会被自动转换成 Box<dyn Error>, 通过 ? 直接传播
let num: i32 = std::fs::read_to_string("num.txt")?.trim().parse()?;
println!("num = {num}");
Ok(())
}注意点
? 运算符只能用在返回类型为 Result(或 Option)的函数中, 返回类型必须和 ? 表达式的错误类型一致
自定义错误类型
如果项目里错误种类比较多, 建议定义一个自己的错误类型, 让错误信息更清晰
rust
use std::fmt;
// 自定义错误类型: 用枚举列出所有可能的错误
#[derive(Debug)]
enum MyError {
NotFound,
PermissionDenied,
}
// 实现 Display trait: 决定错误信息如何输出
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MyError::NotFound => write!(f, "没有找到"),
MyError::PermissionDenied => write!(f, "没有权限"),
}
}
}
// 实现 std::error::Error trait
// 只有实现了 Error trait 的错误, 才能配合 ? 运算符使用
impl std::error::Error for MyError {}
fn main() {
let r = find_user(1);
match r {
Ok(name) => println!("用户: {name}"),
Err(e) => println!("错误: {e}"), // 错误: 没有找到
}
}
fn find_user(id: u32) -> Result<String, MyError> {
if id == 1 {
return Ok("张三".to_string());
}
Err(MyError::NotFound)
}rust
// Cargo.toml 中添加依赖:
// [dependencies]
// thiserror = "2"
use thiserror::Error;
// 使用 thiserror 派生宏, 自动实现 Display 和 Error trait
#[derive(Debug, Error)]
enum MyError {
#[error("没有找到")]
NotFound,
#[error("没有权限")]
PermissionDenied,
}
fn main() {
let r = find_user(1);
match r {
Ok(name) => println!("用户: {name}"),
Err(e) => println!("错误: {e}"), // 错误: 没有找到
}
}
fn find_user(id: u32) -> Result<String, MyError> {
if id == 1 {
return Ok("张三".to_string());
}
Err(MyError::NotFound)
}错误处理相关的开源库
rust
// Cargo.toml 添加依赖:
// [dependencies]
// anyhow = "1"
fn main() -> anyhow::Result<()> {
// 使用 anyhow 之后, 错误类型统一为 anyhow::Error
// 任何实现了 std::error::Error 的错误, 都可以通过 ? 直接传播
let content = std::fs::read_to_string("hello.txt")?;
println!("{content}");
Ok(())
}