Try operator for Result<Option<T>, E>

Greetings!

Any way to get something like this?

type Result<T> = std::result::Result<T, &'static str>;

fn foo(id: u8) -> Result<Option<String>> {
    match id {
        0 => Ok(Some("string".to_owned())),
        1 => Ok(None),
        _ => Err("error"),
    }
}

fn bar(id: u8) -> Result<Option<String>> {
    let ret = foo(id)??;

    // a lot of other work

    Ok(Some(ret))
}

fn main() {
    println!("foo(0): {:?}", foo(0));
    println!("foo(1): {:?}", foo(1));
    println!("foo(128): {:?}", foo(128));

    println!("bar(0): {:?}", bar(0));
    println!("bar(1): {:?}", bar(1));
    println!("bar(128): {:?}", bar(128));
}

Because this look ugly, especially when I have many operations like this:

fn bar(id: u8) -> Result<Option<String>> {
    let ret = match foo(id)? {
        Some(data) => data,
        None => return Ok(None),
    };

    // a lot of other work

    Ok(Some(ret))
}

Well, try! used to be a macro. You could just… try_harder!

macro_rules! try_harder {
    ($x:expr) => {
        match $x {
            Ok(Some(value)) => value,
            Ok(None) => return Ok(None),
            Err(error) => return Err(error.into()),
        }
    }
}
4 Likes
let ret = foo(id)?.ok_or(format!(“No foo for id: {}”, id))?;

Or, if you prefer Ok(None), you can use something like

let ret = foo(id)?;
Ok(ret.map(|v| do_work(v)))
2 Likes

I thought about the macro, but still, it will be look a little ugly. I would like a more elegant solution.
Also, code is different sometimes, and I should write a macro for each case.
So I've asked, because maybe I've missed some little-known feature.

What's ugly or non-elegant about it? Macros are in the language for this exact reason: to provide syntactic abstraction. The macro is the right choice in this case. There simply can't be a separate feature, operator, etc. for every possible combination of types…

2 Likes

Maybe something like this?

fn bar(id: u8) -> Result<Option<String>> {
    let ret = foo(id);
    if let Ok(Some(ret)) = &ret {
        // a lot of other work
    }
    ret
}

Otherwise, I'd probably go for something like the try_harder! that @H2CO3 suggests.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.