Reputation: 401
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)
Upvotes: 3
Views: 1439
Reputation: 432239
This is one way you can convert an Option<Box<Trait>>
to an Option<&Trait>
. I'm avoiding all of the trait implementation 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 consuming the self.c
item. The map
closure is provided with a &Box<Trait>
, so we dereference it twice to get to a Trait
, and then reference it once to get to a &Trait
.
Upvotes: 4