Rust:用问号 ?操作符简化错误信息的判断

Option 返回值

如果你调用的函数和你正在写的函数都返回 Option 类型,如果你调用的函数返回 None,你的函数也返回 None,这时,代码可以用问号 ? 操作符简化。看下面的例子:

fn foo() -> Option<i32> {
    None
}
fn bar() -> Option<String> {
	foo()?;
	Some(String::from("Hello"))
}
fn main() {
    println!("{:?}", bar());
}
========= cargo run =========
None

Result 返回值

看个例子:

fn foo() -> Result<i32, String> {
    Err(String::from("foo error!"))
}
fn bar() -> Result<String, String> {
    foo()?;
    Ok(String::from("Hello"))
}
fn main() {
    println!("{:?}", bar());
}
========= cargo run =========
Err("foo error!")

错误信息类型不一样,如何转换?

如果 foobar 错误信息类型不一致,会有什么结果?看下面的例子:

fn foo() -> Result<i32, String> {
    Err(String::from("foo error!"))
}
fn bar() -> Result<i32, i32> {
    foo()?;
    Ok(123)
}
fn main() {
    println!("{:?}", bar());
}
========= cargo run =========

5 |     foo()?;
  |          ^ the trait `From<String>` is not implemented for `i32`

一堆错误信息。仔细阅读其内容,错误信息说,如果为 i32 实现了 From<String> 特性,还是允许的。因此,只要 foo 的错误信息能够转换为 bar 函数的错误信息,还是允许的。这个简直太好了,因此,我决定用一个新的数据类型作为错误信息。再实验一下:

fn foo() -> Result<i32, String> {
    Err(String::from("foo error!"))
}
#[derive(Debug)]
struct MyErr {
    error_code: i32,
}

impl From<String> for MyErr {
    fn from(s: String) -> Self {
        if s.is_empty() {
            MyErr { error_code: 0 }
        } else {
            MyErr { error_code: 1 }
        }
    }
}

fn bar() -> Result<i32, MyErr> {
    foo()?;
    Ok(123)
}
fn main() {
    println!("{:?}", bar());
}
========= cargo run =========
Err(MyErr { error_code: 1 })

Option 转 Result

如果 foobar· 函数的返回类型分别是 OptionResult`,我们看会出现什么情况:

fn foo() -> Option<i32> {
    None
}
#[derive(Debug)]
struct MyErr {
    error_code: i32,
}

fn bar() -> Result<i32, MyErr> {
    foo()?;
    Ok(123)
}
fn main() {
    println!("{:?}", bar());
}
========= cargo run =========
/ fn bar() -> Result<i32, MyErr> {
10 | |     foo()?;
   | |          ^ use `.ok_or(...)?` to provide an error compatible with `Result<i32, MyErr>`
11 | |     Ok(123)
12 | | }
   | |_- this function returns a `Result`

编译期建议用 ok_or 方法进行转换。代码我们修改一下:

fn foo() -> Option<i32> {
    None
}
fn bar() -> Result<i32, String> {
    foo().ok_or("error".to_string())?;
    Ok(123)
}
fn main() {
    println!("{:?}", bar());
}
========= cargo run =========
Err("error")

ok_or 可以把 Option 类型转换为 Result,其实现代码如下:

    pub fn ok_or<E>(self, err: E) -> Result<T, E> {
        match self {
            Some(v) => Ok(v),
            None => Err(err),
        }
    }

类似的函数还有 ok_or_else,允许用闭包实现更复杂的转换,此处不赘述。

Result 转 Option

继续试验:

fn foo() -> Result<i32,i32> {
    Err(123)
}
fn bar() -> Option<i32> {
    foo()?;
    Some(0)
}
fn main() {
    println!("{:?}", bar());
}
========= cargo run =========
4 | / fn bar() -> Option<i32> {
5 | |     foo()?;
  | |          ^ use `.ok()?` if you want to discard the `Result<Infallible, i32>` error information

错误信息说,如果想提前丢弃计算结果,可以用 .ok()? 方法。代码修改一下:

fn foo() -> Result<i32,i32> {
    Err(123)
}
fn bar() -> Option<i32> {
    foo().ok()?;
    Some(0)
}
fn main() {
    println!("{:?}", bar());
}
========= cargo run =========
None

果然转换过来了。我们看看 ok() 的实现代码:

   pub fn ok(self) -> Option<T> {
        match self {
            Ok(x) => Some(x),
            Err(_) => None,
        }
    }

原来如此简单呀!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许野平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值