Supposed I have a custom error, that sometimes "wraps" an std::io::Error
:
use std::io::{ErrorKind, Error as IoError};
pub enum MyError {
Cancelled,
TimedOut,
Incomplete,
Failed(IoError)
}
Now, I want to be able to convert between MyError
and IoError
, in both directions:
impl From<MyError> for IoError {
fn from(value: MyError) -> Self {
match value {
MyError::Failed(error) => error,
other => IoError::new(ErrorKind::Other, other),
}
}
}
impl From<IoError> for MyError {
fn from(value: IoError) -> Self {
match get_inner(value) {
Ok(inner_error) => inner_error,
Err(other) => MyError::Failed(other),
}
}
}
fn get_inner<T>(error: IoError) -> Result<T, IoError>
where
T: Error + 'static,
{
match error.get_ref().and_then(|err| err.downcast_ref::<T>()) {
Some(_) => Ok(*error.into_inner().unwrap().downcast::<T>().unwrap()),
None => Err(error),
}
}
My problem is: How do I get the concrete MyError
out of IoError
, in case that the IoError
contains a MyError
as its "inner" error – but otherwise retain the IoError
so that it can be wrapped?
Is there a "better" way to implement get_inner()
, or completely get rid of it?
Thing is, get_ref()
+ downcast_ref()
gives me &MyError
when that is possible, but does not allow me to return the MyError
as an owned value. Meanwhile, into_inner()
consumes the original IoError
, so in case we could not convert to MyError
, the original IoError
is gone
Workaround is to combine both approaches.
But, having to do the downcast twice – the first time just for test – seems redundant
Basically, I'd need something that gives me the "inner" error as its concrete type, if and only if the IoError
does contain an "inner" error of the desired type. Otherwise, just give me back the IoError
as-is. Unfortunately, into_inner()
consumes to original IoError
, so there is no way back...
Thank you!