如果Option是Some,返回Err的惯用方式

7

我们可以使用 or_else? 来在遇到 None 时提前返回Err

let o = None;
let x = o.ok_or(666)?;

但如果我们期望相反的呢?如果某个值是 Some,就提前返回。

let o = Some(42);
if o.is_some() {
    return Err(666);
}

我们能用? 来实现这个吗?


据我所知,除了使用if/match语句之外,没有其他方法可以实现这个。 - Ibraheem Ahmed
2个回答

3
我认为这一切都是为了让它成为单行并节省字符。
您可以使用map_or(),将Some(_)转换为Err(666),将None转换为Ok(())。但是,这不是惯用法,我个人也会坚持使用if is_some() { return Err(666); },因为这样做更清晰易懂。
fn foo(o: Option<i32>) -> Result<(), i32> {
    o.map_or(Ok(()), |_| Err(666))?;

    Ok(())
}

fn main() {
    println!("{:?}", foo(None));
    println!("{:?}", foo(Some(42)));
}

输出:

Ok(())  
Err(666)

您也可以创建自己的ErrOnSome trait。将方法命名为err_on_some(),这样即使不知道err_on_some()方法的具体实现,读者也能更清晰地理解和假设其作用。请保留HTML标签。
trait ErrOnSome {
    fn err_on_some<F, E>(&self, f: F) -> Result<(), E>
    where
        F: FnOnce() -> Result<(), E>;
}

impl<T> ErrOnSome for Option<T> {
    fn err_on_some<F, E>(&self, f: F) -> Result<(), E>
    where
        F: FnOnce() -> Result<(), E>,
    {
        match self {
            None => Ok(()),
            Some(_) => f(),
        }
    }
}

fn foo(o: Option<i32>) -> Result<(), i32> {
    o.err_on_some(|| Err(666))?;

    Ok(())
}

使用相同的main(),它当然会产生相同的输出。

编辑:旧答案 - 我误读并认为它是关于返回Option

如果包含的值是一个原始值,即便宜创建,则可以坚持使用xor()。但是,这不是惯用法,我个人也会坚持使用if is_some() { return Err(666); },因为这样做对读者更清晰明了。

fn foo(o: Option<i32>) -> Option<()> {
    o.xor(Some(666))?;

    Some(())
}

fn main() {
    println!("{:?}", foo(None));
    println!("{:?}", foo(Some(42)));
}

输出:

Some(123)
None

2
我们是否可以以类似的方式使用?,在Some上返回Err
我不会称之为习惯用法,但您可以使用Option :: map将选项的 Some 变体映射为 Err 。 这将将 Option&lt; T&gt; 转换为 Option&lt; Result&lt; X,E&gt;> ,您可以将其转置为 Result&lt; Option&lt; X&gt;,E&gt; 并在其上使用
fn x() -> Result<(), i32> {
    let o = Some(42);
    o.map(|_| Err::<(), _>(666)).transpose()?;
    Ok(())
}
Err 中的 turbofish 提供了必要的类型提示,否则 Rust 就无法确定上述 Result<X, E> 中的 X,而结果必须同时具有 ok 和 error 类型。我选择了 (),但它与函数返回类型中的 () 毫不相关;因为 ? 运算符仅关心 Result 的错误类型,所以它可以是任何其他类型。请注意保留 HTML 标签。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接