I'm trying to simplify the error flow in a webapp I'm working on, and my plan was to make a struct that implements std::error::Error and just forwards the result of description() for whatever kind of error it's wrapped around. I've implemented From for the types of errors I want to wrap, so this struct makes it easy to use try! to get a uniform error result. Here's what I have so far for the struct:
#![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 cause() method - I can't return a reference to the inner Error instance because e doesn't live long enough. Is there a different way I can structure this so that I can keep the generic reference to anything that implements Error (which I currently do by putting it in a Box) but I can still fully implement the Error trait (which is expecting a ref to the parent Error)?
I've worked around it by just punting on cause() and having it return None, but I'd much rather conform to the intent of the trait.
rustc 1.2.0-nightly (613e57b44 2015-06-01) (built 2015-06-02)
This is one way you can convert an
Option<Box<Trait>>to anOption<&Trait>. I'm avoiding all of the trait implementation to clearly show the interesting code:We use
Option::as_refto avoid consuming theself.citem. Themapclosure is provided with a&Box<Trait>, so we dereference it twice to get to aTrait, and then reference it once to get to a&Trait.