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