Reputation: 802
You can unwrap a Result
(panic on Err
) with a custom message using expect
.
How can you propagate an error with a custom message (rather than the default message with ?
)?
use std::io::{Result, Error, ErrorKind};
fn main() -> Result<()> {
let a = Err(Error::new(ErrorKind::other, "default message"));
// Let's pretend a is a result automatically created by some other method.
let b = a.unwrap(); // panics with default message
let b = a.expect("custom message"); // panics with custom message
let b = a?; // Returns Err with default message
let b = /* What goes here? */ // Returns Err with custom message
}
Upvotes: 1
Views: 1628
Reputation: 168988
There's a few ways to handle this. First, recognize that there is no standard "custom error message" in this context, because in Result<T, E>
, the E
type can be any type at all, even one that doesn't implement std::error::Error
. So, the analogy to the panicking methods doesn't exactly hold. In particular, note that a?
does not return "Err
with default message!" (See below for an explanation.) What you might be looking for instead is a mechanism to convert from one error type to another.
One way to do this is with Result::map_err
, which maps one error value to another.
struct ErrorA;
struct ErrorB;
fn foo() -> Result<(), ErrorA> { todo!() }
fn example() -> Result<(), ErrorB> {
let a = foo();
let b = a.map_err(|_| ErrorB);
b
}
However, you can make this automatic by realizing that ?
operator performs Into
-based conversion of the error. The expression a?
is roughly equivalent to:
match a {
Ok(v) => v,
Err(e) => return Err(std::convert::Into::into(e)),
}
This means it will automatically convert between error types if a suitable Into
impl exists. For example:
struct ErrorA;
struct ErrorB;
impl From<ErrorA> for ErrorB {
fn from(_: ErrorA) -> Self { Self }
}
fn foo() -> Result<(), ErrorA> { todo!() }
fn example() -> Result<(), ErrorB> {
let a = foo();
let b = a?; // Conversion to ErrorB is implicit
Ok(b)
}
If you really want just a "custom string" error then you can use String
or &'static str
as the error type. This can be problematic in some contexts though, because neither of these types implement std::error::Error
. However, there is a conversion from both to Box<dyn Error>
, so if you don't want to define your own error type you can do something like:
struct ErrorA;
fn foo() -> Result<(), ErrorA> { todo!() }
fn example() -> Result<(), Box<dyn std::error::Error>> {
let a = foo();
// ? converts the &str to Box<dyn Error>
let b = a.map_err(|_| "custom error message")?;
Ok(b)
}
This approach is suitable for simple Rust programs, but generally shouldn't be used in Rust libraries. Custom error types are more suitable for libraries as they allow callers to distinguish different failure scenarios.
Further reading:
Upvotes: 3