I have some functions that will return a different error type when failing.
First I have a builder, which contains this method:
#[derive(Debug)]
pub enum BuilderError {
ElementMissing(&'static str),
}
pub fn spawn(self) -> Result<ServiceStatus, BuilderError>
So it will return a BuildError
on failure.
Now, I have another function that will return another error:
#[derive(Debug)]
pub enum XmlError {
XmlCreationFailed(writer::Error),
ConversionToUtf8(FromUtf8Error),
}
pub fn create_xml(service_status: super::ServiceStatus) -> Result<String, XmlError>
The idea is that I use the builder to create a ServiceStatus
object and use it to create a XML string with create_xml
function.
To do that, I have this code:
#[derive(Debug)]
pub enum WebserviceError {
XmlError(XmlError),
BuilderError(BuilderError),
}
impl std::error::Error for WebserviceError {
...
}
impl From<XmlError> for WebserviceError {
fn from(error: XmlError) -> WebserviceError {
WebserviceError::XmlError(error)
}
}
impl From<BuilderError> for WebserviceError {
fn from(error: BuilderError) -> WebserviceError {
WebserviceError::BuilderError(error)
}
}
fn test() -> Result<String, status::WebserviceError> {
...
let service_status = builder.spawn()?;
let xml = status::create_xml(service_status)?;
Ok(xml)
}
Now, I think I can do better using and_then
instead of using ? operator:
fn test() -> Result<String, status::WebserviceError> {
...
builder
.spawn()
.map_err(status::WebserviceError::BuilderError)
.and_then(|hue| status::create_xml(hue).map_err(status::WebserviceError::XmlError))
}
This solution works too, but now I need to explicitly call map_err
to convert from a BuilderError
or XmlError
to a WebserviceError
...
So, my question is, can I do better? I think a solution like this would be ideal:
fn test() -> Result<String, status::WebserviceError> {
...
builder
.spawn()
.and_then(status::create_xml)
}
After some trial, here is the solution:
trait CustomAndThen<T, E> {
fn and_then2<U, E2, F: FnOnce(T) -> Result<U, E2>>(self, op: F) -> Result<U, E>
where E: std::convert::From<E2>;
}
impl<T, E> CustomAndThen<T, E> for Result<T, E> {
fn and_then2<U, E2, F: FnOnce(T) -> Result<U, E2>>(self, op: F) -> Result<U, E>
where E: std::convert::From<E2>
{
match self {
Ok(t) => op(t).map_err(From::from),
Err(e) => Err(e),
}
}
}
...
Ok(builder)
.and_then2(status::ServiceStatusBuilder::spawn)
.and_then2(status::create_xml)
This will create a custom and_then
function for Result
type that will make the conversion inside it, clearing the code
If you're not really interested in exact error, but raise some final error in the end you can use something like:
builder.spawn().ok()
.and_then(|v| status.create_xml(v).ok())
.ok_or_else(|| SomeError('failed to create xml'))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With