pigeonhands
pigeonhands

Reputation: 3424

Match either of Result::Err or an optional field inside of Result::Ok at the same time

I have a struct that is an API endpoint, so I can't change its structure

struct Response {
    error: Option<String>,
    results: Vec<String>,
}

If error is Some, that means it failed server side.

I have a function that returns the struct:

fn get_results() -> Result<Response, String> {
    unimplemented!()
}

Is it possible to match the get_results Result error, and the optional Response.error in the same match branch?

This was my attempt:

fn example() {
    let ret = get_results();
    match ret.map(|resp| resp.error.map_or_else(|| Ok(resp.results), |e| Err(e))) {
        Err(e) => {
            println!("Error: {}", &e);
        }
        Ok(results) => {
            //Handle results
        }
    }
}

But it fails:

error[E0382]: use of moved value: `resp`
  --> src/lib.rs:12:49
   |
12 |     match ret.map(|resp| resp.error.map_or_else(|| Ok(resp.results), |e| Err(e))) {
   |                          ----------             ^^    ---- use occurs due to use in closure
   |                          |                      |
   |                          |                      value used here after move
   |                          value moved here
   |
   = note: move occurs because `resp.error` has type `std::option::Option<std::string::String>`, which does not implement the `Copy` trait

Upvotes: 2

Views: 324

Answers (3)

Kaplan
Kaplan

Reputation: 3728

With the Rust version from 2021 your program runs now as is.
Still, I would prefer the let-else syntax because it makes the program clearer:

fn example() {
    let ret = get_results();
    let Ok(results) = ret else { println!("Error: {}", ret.err().unwrap()); return; };
    // handle results here…
}

Upvotes: 0

Optimistic Peach
Optimistic Peach

Reputation: 4288

If the error field is public, you can do something like this:

match resp {
    Err(s) | Ok(Response { error: Some(s), .. }) => println!("Error: {}", s),
    Ok(Response { error: None, results }) => println!("Responses: {:?}", results)
}

This shows how powerful Rust's match statements can be.

Playground of a simplified example

Upvotes: 3

JayDepp
JayDepp

Reputation: 1245

You can transform a response into a result like this:

match resp.error {
    Some(e) => Err(e),
    None => Ok(resp.results),
}

You can flatten a nested result with and_then. Put together, it gives this:

ret.and_then(|resp| match resp.error {
    Some(e) => Err(e),
    None => Ok(resp.results),
})

Upvotes: 4

Related Questions