8

I am aware that x.unwrap() when x: Result<T, E> does not work when E does not implement Debug: unwrap() would need to print out the Err variant in case x.is_err() but it cannot. Sometimes, however, especially in tests, I do need to get my hands on the Ok value. I assumed x.expect() would do the trick, as I am the one that specifies the message upon failure. And yet, for some reason I don't completely understand, expect(), too, requires E: Debug. This means that I always end up taking the verbose, repetitive way:

   let x_ok = match x {
      Ok(x_ok) => x_ok,
      Err(_) => panic!("Something went horribly wrong!"),
   }

I cannot imagine there wouldn't be a more standardised solution to this problem, and yet I struggle to find one. How does one quickly get_ok_or_panic if the Err type of a Result does not implement Debug?

Matteo Monti
  • 8,362
  • 19
  • 68
  • 114
  • 1
    Not really different than the existing answers but alternative: `x.map_err(|_| "Something went horribly wrong!").unwrap();` – Ömer Erden Feb 04 '23 at 13:12
  • 1
    The proper solution would be to make `E` implement `Debug`; or even better, [`Error`](https://doc.rust-lang.org/std/error/trait.Error.html), which implies `Debug`. Every error of every library I know does that, and I recommend doing it as well. To define errors properly, use [`thiserror`](https://docs.rs/thiserror); this is pretty much the standard everywhere. Having errors that don't implement `Debug` is a code smell to me. – Finomnis Feb 04 '23 at 14:00
  • 1
    @Finomnis It's not necessarily about a concrete error that doesn't implement `Debug`. This issue tends to appear in generic code - for example, you might require `T: FromStr` and want to use `x.parse().unwrap()`. In that case it can be a hassle to also require `::Err: Debug`, especially if that requirement has to infect other traits and associated types, some of which you might not control. – user4815162342 Feb 04 '23 at 14:23
  • 1
    `expect()` also prints the error, alongside with the message. – Chayim Friedman Feb 04 '23 at 17:05
  • Makes sense; I'd still argue that `::Err: Error` is a sane type bound that is fine to use in generic code. At least if you use it to `panic`, it should at least be printable. Otherwise it would be silently swallowed and discarded. – Finomnis Feb 04 '23 at 20:01

2 Answers2

5

Idiomatic code would just forward the error:

fn do_it() -> Result<(), E> {
   let x_ok = x_ok?;
   // work with x_ok
   Ok(())
}

or handle the error gracefully where possible:

let x_ok = x_ok.unwrap_or(sane_default);

If neither is an option and you absolutely have to panic you can use let … else:

let Ok(x_ok) = x_ok else { panic!("Something went horribly wrong") };
cafce25
  • 15,907
  • 4
  • 25
  • 31
  • Of course, of course, one should handle errors, but as far as I'm given to understand it is common practice, for example, to use `unwrap()` in tests, where `panic()`ing is an indicator of a test having failed. Am I correct saying this? I didn't know of the `let ... else` syntax, thank you for showing me! – Matteo Monti Feb 08 '23 at 19:03
  • Test's can return `Result` too with an `Err` variant signalling a test failure. – cafce25 Feb 08 '23 at 19:04
4

The typical workaround is to use unwrap_or_else():

let x_ok = x.unwrap_or_else(|_| panic!("Something went horribly wrong!"));
user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • I'd only use `unwrap_or_else` in a case I can provide a sane alternative, not to panic. Unless of course you're targeting old Rust versions as well. – cafce25 Feb 04 '23 at 12:36
  • 3
    @cafce25 Duly noted, but that is your personal preference - panicking in `unwrap_or_else()` is both reasonable and commonplace, particularly when the panic is lexically visible, as is the case here. – user4815162342 Feb 04 '23 at 12:40