How can I wrap any application with std :: error :: Error to make it easier to propagate errors?

I am trying to simplify the error flow in a webapp I am working on and my plan was to create a framework that implements std::error::Error

and just feeds the result description()

for whatever error it wraps around. I have implemented From

for the types of errors I want to wrap, so this structure makes it easy to use try!

to get a uniform error result. Here's what I have so far for the structure:

#![feature(convert)]
use std::error::{Error};
use std::fmt::{self,Display,Formatter};
use std::io::{self,Read};
use std::ops::Deref;
use std::fs::{File};

#[derive(Debug)]
pub struct StrErr{
    desc:String,
    c: Option<Box<Error>>
}
impl StrErr{
    pub fn new(msg:String) ->Self{
        StrErr{desc:msg, c:None}
    }
}

impl Error for StrErr{
    fn description(&self) -> &str{
        self.desc.as_str()
    }
    fn cause(& self) -> Option<& Error> {
        self.c.map(|e| e.deref())
    }
}

impl Display for StrErr {
    fn fmt(&self, f:&mut Formatter) -> Result<(),fmt::Error> {
        f.write_str(self.desc.as_str())
    } 
}

impl From<io::Error> for StrErr {
    fn from(o:io::Error) -> Self {
        StrErr{desc: String::from(o.description()),c:Some(Box::new(o))}
    }
}


fn main(){
    let contrived = Some("foo.txt")
        .ok_or_else(|| StrErr::new(String::from("error message")))
        .and_then(|filename| Ok(try!(File::open(filename))))
        .and_then(|mut file| {
            let mut content = String::new();
            try!(file.read_to_string(&mut content));
            Ok(content)
        });
    if let Ok(content) = contrived {
        println!("Got content: {}", content);
    } else {
        println!("got an error");
    }
}

      

playground

The problem is with the method cause()

- I can't get back a reference to the inner instance Error

because it e

doesn't live long enough. Is there another way to structure this so that I can keep a generic reference to whatever it implements Error

(which I am currently doing by putting it in a drawer), but I can still fully implement the trait Error

(which is expecting a parent reference Error

)?

I worked on this by just typing cause()

and returning it None

, but I would rather live up to the intent of this trait.

rustc 1.2.0-nightly (613e57b44 2015-06-01) (built 2015-06-02)

      

+3


source to share


1 answer


This is one way to convert Option<Box<Trait>>

to Option<&Trait>

. I am avoiding the entire implementation of the trait in order to clearly show the interesting code:

use std::error::Error;

pub struct StrErr {
    c: Option<Box<Error>>
}

impl StrErr {
    fn cause(&self) -> Option<&Error> {
        self.c.as_ref().map(|e| &**e)
    }
}

fn main() {}

      



We use Option::as_ref

to avoid using the item self.c

. The closure is map

supplied &Box<Trait>

, so we look for it twice to navigate to Trait

, and then reference it once to navigate to &Trait

.

+4


source







All Articles