Idiomatic way to return Err if an Option is Some

Viewed 422

We can use or_else and ? to return Err early if we encounter None:

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

But what if we expect the opposite? Return early if something is Some:

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

Can we somehow do this with ? as well?

2 Answers

I assume this is all about having it be a single line and saving characters.

You could use map_or(), to convert Some(_) into Err(666) and None into Ok(()). However, this isn't idiomatic, I'd also personally stick to if is_some() { return Err(666); }, as what that is doing is more clear to the reader.

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

    Ok(())
}

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

Outputs:

Ok(())  
Err(666)

You could also create your own ErrOnSome trait. Naming the method e.g. err_on_some() it would be more clear and assumable to the reader what's going on, even without knowing the implementation of the err_on_some() method.

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(())
}

Using the same main() it produces the same output of course.


Edit: Old answer - I misread and thought it was about returning Option

If the contained value is a primitive, i.e. cheap to create, then you could stick to xor(). However, this isn't idiomatic, I'd also personally stick to if is_some() { return Err(666); }, as what that is doing is more clear to the reader.

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

    Some(())
}

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

Outputs:

Some(123)
None

Can we somehow [return Err on Some] with ? as well?

I wouldn't call it idiomatic, but you can use Option::map to map the Some variant of the option into an Err. This will transform Option<T> to Option<Result<X, E>>, so you need to transpose it into a Result<Option<X>, E> and use ? on that:

fn x() -> Result<(), i32> {
    let o = Some(42);
    o.map(|_| Err::<(), _>(666)).transpose()?;
    Ok(())
}

The turbofish on Err provides a necessary type hint because otherwise Rust can't determine the X of Result<X, E> mentioned above, and a result must have both an ok and an error type. I've chosen (), but it's completely unrelated to the () in the return type of the function; it could have been any other type because the ? operator only cares about the error type of the Result.

Related