I'm writing a function that iterates over a vector of Result
and returns success if they all were successful, or an error if any failed. Limitations in error::Error
are frustrating me and I'm not sure how to work around them. Currently I have something like:
let mut errors = Vec::new();
for result in results {
match result {
Err(err) => errors.push(err),
Ok(success) => { ... }
}
}
if errors.is_empty() {
return Ok(())
else {
return Err(MyErrorType(errors))
}
The problem with my current approach is that I can only set one error to be the cause
of MyErrorType
, and my error's description
needs to be a static String
so I can't include the descriptions of each of the triggering failures. All of the failures are potentially relevant to the caller.
The aggregation problem is the difficult problem of finding a valid way to treat an empirical or theoretical aggregate as if it reacted like a less-aggregated measure, say, about behavior of an individual agent as described in general microeconomic theory.
Calibration errors, variation in the contact pressure, variation in the atmospheric pressure, Parallax error, misalignment errors are the sources of Systematic errors. Errors randomly occurred with the measuring instrument. (Unable to predict the when the error going to happen) Hard to control.
An error means you're trying to do something that might be a little complicated (or very complicated), and it doesn't quite work yet, but by no means is it a sign that you should stop trying! In fact, there are entire engineering roles built around finding and fixing errors.
As your code increases in complexity, the number of errors you'll encounter rises at a similar rate. An error means you're trying to do something that might be a little complicated (or very complicated), and it doesn't quite work yet, but by no means is it a sign that you should stop trying!
There is no convention that I know of, and indeed I have never had the issue of attempting to report multiple errors at once...
... that being said, there are two points that may help you:
There is no limitation that the description be a 'static
String
, you are likely confusing &'static str
and &str
. In fn description(&self) -> &str
, the lifetime of str
is linked to the lifetime of self
(lifetime elision) and therefore an embedded String
satisfies the constraints
Error
is an interface to deal with errors uniformly. In this case, indeed, only a single cause
was foreseen, however it does not preclude a more specific type to aggregate multiple causes and since Error
allows downcasting (Error::is
, Error::downcast
, ...) the more specific type can be retrieved by the handler and queried in full
As such, I would suggest that you create a new concrete type solely dedicated to holding multiple errors (in a Vec<Box<Error>>
), and implementing the Error
interface. It's up to you to decide on the description and cause it will expose.
A single type will let your clients test more easily for downcasting than having an unknown (and potentially growing as time goes) number of potential downcast targets.
expanding a bit on point 1 of Matthieu's good answer.
The point where you're likely running into trouble (I know I did when I tried to implement Error
) is that you want to have a dynamic description()
.
// my own error type
#[derive(Debug)] struct MyError { value: u8 }
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "bummer! Got a {}", self.value)
}
}
// I am now tempted to add the problematic value dynamically
// into the description, but I run into trouble with lifetimes
// this DOES NOT COMPILE because the String I'm building
// goes out of scope and I can't return a reference to it
impl error::Error for MyError {
fn description(&self) -> &str {
&format!("can't put a {} here!", self.value)
}
}
solution 1
Don't dynamically build description()
. Just use a static str. This is what most implementations of Error on github seem to do.
If you need to retrieve and display (or log) the value you can always access it from your MyError
type. Plus Display
(that you must implement for all Error impls) does allow you to create dynamic strings.
I created a contrived example on the playground that shows how to track multiple errors.
solution 2
(what Matthieu is suggesting) you can store the error message in the error itself.
#[derive(Debug)] struct MyError { value: u8, msg: String }
impl MyError {
fn new(value: u8) -> MyError {
MyError { value: value, msg: format!("I don't like value {}", value) }
}
}
// now this works because the returned &str has the same lifetime
// as self
impl error::Error for MyError {
fn description(&self) -> &str {
&self.msg
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With