- Start Date: (fill me in with today's date, 2014-07-17)
- RFC PR #: rust-lang/rfcs#201
- Rust Issue #: rust-lang/rust#17747
Summary
This RFC improves interoperation between APIs with different error types. It proposes to:
-
Increase the flexibility of the
try!
macro for clients of multiple複数のlibraries with disparate error types. -
Standardize on basic functionality that any error type should have by introducing an
Error
trait. -
Support easy error chaining when crossing abstraction boundaries.
The proposed changes are all library changes; no language
Motivation
Typically,Result
instanceio::IoError
and io::IoResult<T> = Result<T, io::IoError>
, and similarlytry!
macro, the story for interacting with errors for a single
However, we lack infrastructure when consuming or building on errors from multiple
Consuming multiple複数の error types
Our current infrastructure for error handling does not cope well with mixed notions of error.
Abstractly, as described
fn func() -> Result<T, Error> {
try!(may_return_error_type_A());
try!(may_return_error_type_B());
}
Concretely, imagine a CLI application that interacts both with files and HTTP servers, using std::io
and an imaginaryhttp
crate:
fn download() -> Result<(), CLIError> {
let contents = try!(http::get(some_url));
let file = try!(File::create(some_path));
try!(file.write_str(contents));
Ok(())
}
The download
function can encounter both io
and http
errors, and wants to report them both under the common notion of CLIError
. But the try!
macro only works for a single
There are roughly two scenarios where multiple
Application error reporting: presenting errors to a user
An application is generally the "last stop" for error handling: it's the point at which remaining errors are presented
As such, the data needed for application-level errors is usually related to human interaction. For a CLI application, a short text description and longer verbose description are usually all that's needed. For GUI applications, richer data is sometimes required, but usually not a full enum
describing
Concretely, then, for something like the download
function above, for a CLI application, one might want CLIError
to roughly be:
Ideally, one could use the try!
macro as in the download
example to coalesce a variety of error types into this single,struct
.
Library error reporting: abstraction boundaries
When one library builds on others, it needs to translate from their error types to its own. For example, a web server framework may build on a library for accessing a SQL database, and needs some way to "lift" SQL errors to its own notion of error.
In general,
In some cases, the right way to lift a given
Abstracting over errors
Finally, libraries sometimes need to work with errors in a generic way. For example, the serialize::Encoder
type takesE
. At the moment, such types are completely arbitrary:Error
trait giving common functionality expected of all errors. Consequently,
(See this issue for a concrete
Languages that provide exceptionslibstd
to ensure
Detailed design設計(する)
We can address all of the problems laid out in the Motivation sectionlibstd
, so this RFC will actually give a full implementation.
Note, however, that this implementation
The proposal consistsError
trait and extensions to the try!
macro.
The Error
trait
The standard Error
trait followsException
base
Every concreteenum
error types and then implement
The cause
method allowsError
, which makes it possible to read off a kind of abstract backtrace (often more immediately
The Any
boundAny
trait, but leaves unspecified the exact implementationAny
; otherwise,downcast
APIs as blanket impl
s on Error
objects.)
It's worth comparingError
trait to the most widespread error type in libstd
, IoError
:
Code that returns or asks for an IoError
explicitlykind
field and thusError
(e.g., application code) sees only the human-consumable parts of the error. In particular, application code will often employ Box<Error>
as the error type when reporting errors to the user. The try!
macro support, explained below, makes doing so ergonomic.
An extended拡張された try!
macro
The other piece to the proposal is a way for try!
to automatically
The idea is to introduceFromError<E>
that says how to convertE
to Self
. The try!
macro then passes the error it is given
This code depends on multidispatch, because the conversionFromError
given
Givenimpl
of FromError<E>
for E
, all existing uses of try!
would continue to work as-is.
With this infrastructure in place, application code can generally use Box<Error>
as its error type, and try!
will take
fn download() -> Result<(), Box<Error>> {
let contents = try!(http::get(some_url));
let file = try!(File::create(some_path));
try!(file.write_str(contents));
Ok(())
}
Library code that definesFromError
implementationstry!
:
impl FromError<ErrorA> MyError { ... }
impl FromError<ErrorB> MyError { ... }
fn my_lib_func() -> Result<T, MyError> {
try!(may_return_error_type_A());
try!(may_return_error_type_B());
}
Drawbacks
The main drawback is that the try!
macro is a bit more complicated.
Unresolved questions
Conventions
This RFC does not define
Extensions
The functionality in the Error
trait is quite minimal, and should probably grow over time. Some additional
Features on the Error
trait
-
Generic creation of
Error
s. It might be useful for theError
trait to expose an associated関連付けられたconstructor.function Object() { [native code] }See this issue for an example where this functionality would be useful. -
Mutation of
Error
s. TheError
trait could be expanded to provide setters as well as getters.
The main reason not to include the above two features is so that Error
can be used with extremely minimal data structures, e.g. simple enum
s. For such data structures, it's possible to produceError
-bounded type would also require these enum
s to include something like a GenericError
variant, which is unfortunate. So for now, the design
Concrete具体的な/具象的な error types
On the other hand, for code that doesn't care about the footprint of its error types, it may be useful to provide something like the following
This type can easily be added