Pre-RFC: general error types


#1

Result<T, String> is, generally speaking, bad. It’s lazy and it’s not idiomatic; if you want to fail with a descriptive error, you should instead use a type that implements std::error::Error. Unfortunately, there are times when it should be okay to be lazy. Maybe you want a descriptive error but don’t need to handle different kinds of errors, or you have a function that calls other functions that return a Result, but the error type is different in each case. For those situations (and probably others I haven’t thought of), you want a general-purpose error type, which libstd doesn’t currently have. Therefore, I’d like to propose the following additions to libstd:

  • A new type, std::error::WrappedError, which implements std::error::Error such that calls to those methods are direct calls to an inner Error object:
use std::error::Error;

pub struct WrappedError { ... }

impl WrappedError {
    pub fn new<T>(error: T) -> WrappedError where T: Into<Box<Error>> { ... }
}

impl Error for WrappedError { ... }
  • A new type, std::error::GeneralError, which holds a String and an Option<Box<Error>>, and implements std::error::Error such that GeneralError::description() returns the held string, and GeneralError::cause() returns an optional reference to the held error:
use std::error::Error;

pub struct GeneralError { ... }

impl GeneralError {
    pub fn new<T>(description: T) where T: Into<String> { ... }
    pub fn with_cause<T, C>(description: T, cause: C) where T: Into<String>, C: Into<Box<Error>> { ... }
}

What do you guys think?


#2

How is WrappedError any different to Box<Error>?

As for GeneralError, I’m not sure it’s hugely useful. Really, all it’s adding is the ability to plaster a different message over the top of an existing error. Given how errors are typically rendered, this pretty much just replaces the inner error with a string.

I’ve also tried to avoid doing this myself, because I worry about the perf hit of having each “layer” doing allocations to describe what went wrong when, really, it would be better to just define specialised enum/unitary types that should be a lot cheaper.

If you wanted to do this in general, wouldn’t it make more sense to have the “outer” error be any Error type, rather than just String? I mean, if nothing else, it should at least be Cow<'static, str> to allow for fixed messages without allocation overhead.

Also, GeneralError is supremely non-descriptive. WrappedError's a better name for it.

Edit: Oh, and I think things like this are better written as external crates first, to prove they’re useful.


#3

You can use SimpleError for a change. Since you are not going to programically handle the error, I am not sure of the usefulness of having a cause. You can incorporate the cause error string directly into the simple error itself.