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"); } }
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)
source to share
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
.
source to share