I'm working with a third-party library that provides tree-based data structures that I have to use "as is". The API returns Result<T, Error>
. I have to make some sequential calls and convert the error to my application's internal error.
use std::error::Error;
use std::fmt;
pub struct Tree {
branches: Vec<Tree>,
}
impl Tree {
pub fn new(branches: Vec<Tree>) -> Self {
Tree { branches }
}
pub fn get_branch(&self, id: usize) -> Result<&Tree, TreeError> {
self.branches.get(id).ok_or(TreeError {
description: "not found".to_string(),
})
}
}
#[derive(Debug)]
pub struct TreeError {
description: String,
}
impl Error for TreeError {
fn description(&self) -> &str {
self.description.as_str()
}
}
impl fmt::Display for TreeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description.fmt(f)
}
}
#[derive(Debug)]
pub struct MyAwesomeError {
description: String,
}
impl MyAwesomeError {
pub fn from<T: fmt::Debug>(t: T) -> Self {
MyAwesomeError {
description: format!("{:?}", t),
}
}
}
impl Error for MyAwesomeError {
fn description(&self) -> &str {
&self.description
}
}
impl fmt::Display for MyAwesomeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description.fmt(f)
}
}
If I write this code:
pub fn take_first_three_times(tree: &Tree) -> Result<&Tree, MyAwesomeError> {
let result = tree
.get_branch(0)
.map(|r| r.get_branch(0))
.map(|r| r.map(|r| r.get_branch(0)));
// ...
}
The type of result
will be Result<Result<Result<Tree, TreeError>, TreeError>, TreeError>
. I don't want to process errors by cascades of match
.
I can write an internal function that adjusts the API's interface and processes the error in the level of base function:
fn take_first_three_times_internal(tree: &Tree) -> Result<&Tree, TreeError> {
tree.get_branch(0)?.get_branch(0)?.get_branch(0)
}
pub fn take_first_three_times(tree: &Tree) -> Result<&Tree, MyAwesomeError> {
take_first_three_times_internal(tree).map_err(MyAwesomeError::from)
}
How can I achieve this without an additional function?