Skip to content

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
}

注意点

unwrapexpect 遇到 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_orOption 转 Result, None 时返回传入的错误值
ok_or_else与 ok_or 相同, 错误值由闭包提供(惰性求值)
transposeOption<Result<T, E>> 转为 Result<Option<T>, E>
Result::okResult 转 Option, Ok -> Some, Err -> None
Result::errResult 转 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}"))
}

注意点

? 运算符只能用在返回类型为 OptionResult 的函数中

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
}

注意点

unwrapexpect 遇到 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)
}

错误处理相关的开源库

  • anyhow: 适合应用程序, 不需要定义错误类型, 直接用 ? 传播
  • thiserror: 适合库, 用于快速定义自己的错误类型
  • miette: 带漂亮诊断信息的错误处理库
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(())
}

Released under the MIT License.