40

I have a function that calls another function which returns a Result. I need to check if the Result is Ok or Err and if it is an Err, I need to return early from my function. This is what I'm doing now:

match callable(&mut param) {
    Ok(_v) => (),
    Err(_e) => return,
};

Is there a more idiomatic Rust way to do this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Josh Abraham
  • 959
  • 1
  • 8
  • 18

4 Answers4

28

You can create a macro:

macro_rules! unwrap_or_return {
    ( $e:expr ) => {
        match $e {
            Ok(x) => x,
            Err(_) => return,
        }
    }
}

fn callable(param: &mut i32) -> Result<i32, ()> {
    Ok(*param)
}

fn main() {
    let mut param = 0;
    let res = unwrap_or_return!(callable(&mut param));

    println!("{:?}", res);
}

Note that I wouldn't recommend discarding the errors. Rust's error handling is pretty ergonomic, so I would return the error, even if it is only to log it:

fn callable(param: &mut i32) -> Result<i32, ()> {
    Ok(*param)
}

fn run() -> Result<(), ()> {
    let mut param = 0;
    let res = callable(&mut param)?;

    println!("{:?}", res);

    Ok(())
}

fn main() {
    if let Err(()) = run() {
        println!("Oops, something went wrong!");
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Boiethios
  • 38,438
  • 19
  • 134
  • 183
  • Thank you. I also changed your macro to allow the caller to specify whether to `return` or `continue`. Is that idiomatic rust? – Josh Abraham Jul 15 '18 at 20:50
  • What is the difference between the macro you created here and the `?` operator? – Paul Razvan Berg Dec 23 '20 at 13:43
  • 1
    @Raul The `?` operator can only be used in a function that returns a [`Try`](https://doc.rust-lang.org/std/ops/trait.Try.html) implementor. The OP wants to return from a function that returns unit, and unit does not implement `Try`. – Boiethios Dec 28 '20 at 10:04
  • 1
    It is a good idea to add an indirection by adding `run()` that returns a `Result`, for two reasons: 1) inside `run()`, you can use `?` to handle error returns; 2) you can then log/process all error returns in a single place. IMO, this is better than creating a macro. – user1783732 Nov 01 '21 at 17:56
  • @user1783732 The main/run thing is outdated: nowadays, the main function can return a result. – Boiethios Nov 02 '21 at 11:43
  • 1
    @Boiethios that's okay. Whether the main() can return a result does not matter. My point was that: when a function `foo` returns () and we still want to use ? to do early returns inside `foo`, it is a good idea to add a layer of `bar() -> Result()` that includes the actual code to allow ? early returns, then `foo` will call `bar` and log the error. – user1783732 Nov 03 '21 at 21:07
19

Rust 1.65.0 has stabilized let-else statements, which enables you to write:

let Ok(_v) = callable(&mut param) else { return };
Maurice Kayser
  • 455
  • 4
  • 11
  • 3
    This is my go-to method for `Option` because I know my `else` block is literally dealing with nothing. But for `Result` I use the OP's method because I want to record the error object in the log before kicking it back. – Fred Drake Feb 22 '23 at 01:52
4

If both functions return Result<doesn't matter, same T> you can just put a ? at the end of line of call.

fn caller() -> Result<Str, i32> {
    let number = job()?; // <-- if job return error this function return/end here 
                         // otherwise the value of Ok will assign to number 
    Ok(format!("the number is {}", number))
}

fn job() -> Result<i32, i32> {
    // do something
    Err(3)
}

You can use same pattern for Option<T> too.

Kian Ostad
  • 151
  • 8
  • 2
    It's pretty clear from OPs example code that this isn't the case: `Err(_e) => return,` – Shepmaster Jul 15 '18 at 20:13
  • All thought this isn't the answer to the question above. I've remembered that there was SOME syntax like this, but not what exactly, and I couldn't find it until now. So kudos for the answer! – Prophet Lamb Nov 25 '21 at 11:30
2

You can use my unwrap_or crate to accomplish this.

You can do:

unwrap_or_ok!(callable(&mut param), _, return);

And if you want the result and to return the error, you can do:

let v = unwrap_or_ok!(callable(&mut param), error, return error);
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366