diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/failure/book | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/failure/book')
-rw-r--r-- | third_party/rust/failure/book/src/SUMMARY.md | 14 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/bail-and-ensure.md | 18 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/custom-fail.md | 75 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/derive-fail.md | 177 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/error-errorkind.md | 147 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/error-msg.md | 59 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/error.md | 100 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/fail.md | 152 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/guidance.md | 24 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/howto.md | 8 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/intro.md | 77 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/string-custom-error.md | 168 | ||||
-rw-r--r-- | third_party/rust/failure/book/src/use-error.md | 66 |
13 files changed, 1085 insertions, 0 deletions
diff --git a/third_party/rust/failure/book/src/SUMMARY.md b/third_party/rust/failure/book/src/SUMMARY.md new file mode 100644 index 0000000000..e5c8a3b438 --- /dev/null +++ b/third_party/rust/failure/book/src/SUMMARY.md @@ -0,0 +1,14 @@ +# Summary + +- [failure](./intro.md) +- [How to use failure](./howto.md) + - [The Fail trait](./fail.md) + - [Deriving Fail](./derive-fail.md) + - [The Error type](./error.md) + - [`bail!` and `ensure!`](./bail-and-ensure.md) +- [Patterns & Guidance](./guidance.md) + - [Strings as errors](./error-msg.md) + - [A Custom Fail type](./custom-fail.md) + - [Using the Error type](./use-error.md) + - [An Error and ErrorKind pair](./error-errorkind.md) + - [Strings and custom fail type](./string-custom-error.md) diff --git a/third_party/rust/failure/book/src/bail-and-ensure.md b/third_party/rust/failure/book/src/bail-and-ensure.md new file mode 100644 index 0000000000..1326f0e05c --- /dev/null +++ b/third_party/rust/failure/book/src/bail-and-ensure.md @@ -0,0 +1,18 @@ +# `bail!` and `ensure!` + +If you were a fan of the `bail!` and ensure! macros from error-chain, good news. failure has a version of these macros as well. + +The `bail!` macro returns an error immediately, based on a format string. The `ensure!` macro additionally takes a conditional, and returns the error only if that conditional is false. You can think of `bail!` and `ensure!` as being analogous to `panic!` and `assert!`, but throwing errors instead of panicking. + +`bail!` and `ensure!` macros are useful when you are prototyping and you want to write your custom errors later. It is also the simplest example of using the failure crate. + +## Example +```rust +#[macro_use] extern crate failure; + +fn safe_cast_to_unsigned(n:i32) -> Result<u32, error::Failure> +{ + ensure!(n>=0, "number cannot be smaller than 0!"); + (u32) n +} +```
\ No newline at end of file diff --git a/third_party/rust/failure/book/src/custom-fail.md b/third_party/rust/failure/book/src/custom-fail.md new file mode 100644 index 0000000000..324c0417b6 --- /dev/null +++ b/third_party/rust/failure/book/src/custom-fail.md @@ -0,0 +1,75 @@ +# A Custom Fail type + +This pattern is a way to define a new kind of failure. Defining a new kind of +failure can be an effective way of representing an error for which you control +all of the possible failure cases. It has several advantages: + +1. You can enumerate exactly all of the possible failures that can occur in +this context. +2. You have total control over the representation of the failure type. +3. Callers can destructure your error without any sort of downcasting. + +To implement this pattern, you should define your own type that implements +`Fail`. You can use the [custom derive][derive-fail] to make this easier. For +example: + +```rust +#[derive(Fail, Debug)] +#[fail(display = "Input was invalid UTF-8")] +pub struct Utf8Error; +``` + +This type can become as large and complicated as is appropriate to your use +case. It can be an enum with a different variant for each possible error, and +it can carry data with more precise information about the error. For example: + +```rust +#[derive(Fail, Debug)] +#[fail(display = "Input was invalid UTF-8 at index {}", index)] +pub struct Utf8Error { + index: usize, +} +``` + +## When might you use this pattern? + +If you need to raise an error that doesn't come from one of your dependencies, +this is a great pattern to use. + +You can also use this pattern in conjunction with [using `Error`][use-error] or +defining an [Error and ErrorKind pair][error-errorkind]. Those functions which +are "pure logic" and have a very constrained set of errors (such as parsing +simple formats) might each return a different custom Fail type, and then the +function which merges them all together, does IO, and so on, would return a +more complex type like `Error` or your custom Error/ErrorKind. + +## Caveats on this pattern + +When you have a dependency which returns a different error type, often you will +be inclined to add it as a variant on your own error type. When you do that, +you should tag the underlying error as the `#[fail(cause)]` of your error: + +```rust +#[derive(Fail, Debug)] +pub enum MyError { + #[fail(display = "Input was invalid UTF-8 at index {}", _0)] + Utf8Error(usize), + #[fail(display = "{}", _0)] + Io(#[fail(cause)] io::Error), +} +``` + +Up to a limit, this design can work. However, it has some problems: + +- It can be hard to be forward compatible with new dependencies that raise + their own kinds of errors in the future. +- It defines a 1-1 relationship between a variant of the error and an + underlying error. + +Depending on your use case, as your function grows in complexity, it can be +better to transition to [using Error][use-error] or [defining an Error & +ErrorKind pair][error-errorkind]. + +[derive-fail]: ./derive-fail.html +[use-error]: ./use-error.html +[error-errorkind]: ./error-errorkind.html diff --git a/third_party/rust/failure/book/src/derive-fail.md b/third_party/rust/failure/book/src/derive-fail.md new file mode 100644 index 0000000000..6fffd99181 --- /dev/null +++ b/third_party/rust/failure/book/src/derive-fail.md @@ -0,0 +1,177 @@ +# Deriving `Fail` + +Though you can implement `Fail` yourself, we also provide a derive macro to +generate the impl for you. To get access to this macro, you must tag the extern +crate declaration with `#[macro_use]`, as in: + +```rust +#[macro_use] extern crate failure; +``` + +In its smallest form, deriving Fail looks like this: + +```rust +#[macro_use] extern crate failure; + +use std::fmt; + +#[derive(Fail, Debug)] +struct MyError; + +impl fmt::Display for MyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "An error occurred.") + } +} +``` + +All failures need to implement `Display`, so we have added an impl of +Display. However, implementing `Display` is much more boilerplate than +implementing `Fail` - this is why we support deriving `Display` for you. + +## Deriving `Display` + +You can derive an implementation of `Display` with a special attribute: + +```rust +#[macro_use] extern crate failure; + +#[derive(Fail, Debug)] +#[fail(display = "An error occurred.")] +struct MyError; +``` + +This attribute will cause the `Fail` derive to also generate an impl of +`Display`, so that you don't have to implement one yourself. + +### String interpolation + +String literals are not enough for error messages in many cases. Often, you +want to include parts of the error value interpolated into the message. You can +do this with failure using the same string interpolation syntax as Rust's +formatting and printing macros: + +```rust +#[macro_use] extern crate failure; + +#[derive(Fail, Debug)] +#[fail(display = "An error occurred with error code {}. ({})", code, message)] +struct MyError { + code: i32, + message: String, +} +``` + +Note that unlike code that would appear in a method, this does not use +something like `self.code` or `self.message`; it just uses the field names +directly. This is because of a limitation in Rust's current attribute syntax. +As a result, you can only interpolate fields through the derivation; you cannot +perform method calls or use other arbitrary expressions. + +### Tuple structs + +With regular structs, you can use the name of the field in string +interpolation. When deriving Fail for a tuple struct, you might expect to use +the numeric index to refer to fields `0`, `1`, et cetera. However, a compiler +limitation prevents this from parsing today. + +For the time being, tuple field accesses in the display attribute need to be +prefixed with an underscore: + +```rust +#[macro_use] extern crate failure; + +#[derive(Fail, Debug)] +#[fail(display = "An error occurred with error code {}.", _0)] +struct MyError(i32); + + +#[derive(Fail, Debug)] +#[fail(display = "An error occurred with error code {} ({}).", _0, _1)] +struct MyOtherError(i32, String); +``` + +### Enums + +Implementing Display is also supported for enums by applying the attribute to +each variant of the enum, rather than to the enum as a whole. The Display impl +will match over the enum to generate the correct error message. For example: + +```rust +#[macro_use] extern crate failure; + +#[derive(Fail, Debug)] +enum MyError { + #[fail(display = "{} is not a valid version.", _0)] + InvalidVersion(u32), + #[fail(display = "IO error: {}", error)] + IoError { error: io::Error }, + #[fail(display = "An unknown error has occurred.")] + UnknownError, +} +``` + +## Overriding `backtrace` + +The backtrace method will be automatically overridden if the type contains a +field with the type `Backtrace`. This works for both structs and enums. + +```rust +#[macro_use] extern crate failure; + +use failure::Backtrace; + +/// MyError::backtrace will return a reference to the backtrace field +#[derive(Fail, Debug)] +#[fail(display = "An error occurred.")] +struct MyError { + backtrace: Backtrace, +} + +/// MyEnumError::backtrace will return a reference to the backtrace only if it +/// is Variant2, otherwise it will return None. +#[derive(Fail, Debug)] +enum MyEnumError { + #[fail(display = "An error occurred.")] + Variant1, + #[fail(display = "A different error occurred.")] + Variant2(Backtrace), +} +``` + +This happens automatically; no other annotations are necessary. It only works +if the type is named Backtrace, and not if you have created an alias for the +Backtrace type. + +## Overriding `cause` + +In contrast to `backtrace`, the cause cannot be determined by type name alone +because it could be any type which implements `Fail`. For this reason, if your +error has an underlying cause field, you need to annotate that field with +the `#[fail(cause)]` attribute. + +This can be used in fields of enums as well as structs. + + +```rust +#[macro_use] extern crate failure; + +use std::io; + +/// MyError::cause will return a reference to the io_error field +#[derive(Fail, Debug)] +#[fail(display = "An error occurred.")] +struct MyError { + #[fail(cause)] io_error: io::Error, +} + +/// MyEnumError::cause will return a reference only if it is Variant2, +/// otherwise it will return None. +#[derive(Fail, Debug)] +enum MyEnumError { + #[fail(display = "An error occurred.")] + Variant1, + #[fail(display = "A different error occurred.")] + Variant2(#[fail(cause)] io::Error), +} +``` diff --git a/third_party/rust/failure/book/src/error-errorkind.md b/third_party/rust/failure/book/src/error-errorkind.md new file mode 100644 index 0000000000..4f1cc4c316 --- /dev/null +++ b/third_party/rust/failure/book/src/error-errorkind.md @@ -0,0 +1,147 @@ +# An Error and ErrorKind pair + +This pattern is the most robust way to manage errors - and also the most high +maintenance. It combines some of the advantages of the [using Error][use-error] +pattern and the [custom failure][custom-fail] patterns, while avoiding some of +the disadvantages each of those patterns has: + +1. Like `Error`, this is forward compatible with new underlying kinds of +errors from your dependencies. +2. Like custom failures, this pattern allows you to specify additional information about the error that your dependencies don't give you. +3. Like `Error`, it can be easier to convert underlying errors from dependency +into this type than for custom failures. +4. Like custom failures, users can gain some information about the error +without downcasting. + +The pattern is to create two new failure types: an `Error` and an `ErrorKind`, +and to leverage [the `Context` type][context-api] provided by failure. + +```rust +#[derive(Debug)] +struct MyError { + inner: Context<MyErrorKind>, +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] +enum MyErrorKind { + // A plain enum with no data in any of its variants + // + // For example: + #[fail(display = "A contextual error message.")] + OneVariant, + // ... +} +``` + +Unfortunately, it is not easy to correctly derive `Fail` for `MyError` so that +it delegates things to its inner `Context`. You should write those impls +yourself: + +```rust +impl Fail for MyError { + fn name(&self) -> Option<&str> { + self.inner.name() + } + + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl Display for MyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} +``` + +You should also provide some conversions and accessors, to go between a +Context, your ErrorKind, and your Error: + +```rust +impl MyError { + pub fn kind(&self) -> MyErrorKind { + *self.inner.get_context() + } +} + +impl From<MyErrorKind> for MyError { + fn from(kind: MyErrorKind) -> MyError { + MyError { inner: Context::new(kind) } + } +} + +impl From<Context<MyErrorKind>> for MyError { + fn from(inner: Context<MyErrorKind>) -> MyError { + MyError { inner: inner } + } +} +``` + +With this code set up, you can use the context method from failure to apply +your ErrorKind to `Result`s in underlying libraries: + +```rust +use failure::ResultExt; +perform_some_io().context(ErrorKind::NetworkFailure)?; +``` + +You can also directly throw `ErrorKind` without an underlying error when +appropriate: + +```rust +Err(ErrorKind::DomainSpecificError)? +``` + +### What should your ErrorKind contain? + +Your error kind probably should not carry data - and if it does, it should only +carry stateless data types that provide additional information about what the +`ErrorKind` means. This way, your `ErrorKind` can be `Eq`, making it +easy to use as a way of comparing errors. + +Your ErrorKind is a way of providing information about what errors mean +appropriate to the level of abstraction that your library operates at. As some +examples: + +- If your library expects to read from the user's `Cargo.toml`, you might have + a `InvalidCargoToml` variant, to capture what `io::Error` and `toml::Error` + mean in the context of your library. +- If your library does both file system activity and network activity, you + might have `Filesystem` and `Network` variants, to divide up the `io::Error`s + between which system in particular failed. + +Exactly what semantic information is appropriate depends entirely on what this +bit of code is intended to do. + +## When might you use this pattern? + +The most likely use cases for this pattern are mid-layer which perform a +function that requires many dependencies, and that are intended to be used in +production. Libraries with few dependencies do not need to manage many +underlying error types and can probably suffice with a simpler [custom +failure][custom-fail]. Applications that know they are almost always just going +to log these errors can get away with [using the Error type][use-error] rather +than managing extra context information. + +That said, when you need to provide the most expressive information about an +error possible, this can be a good approach. + +## Caveats on this pattern + +This pattern is the most involved pattern documented in this book. It involves +a lot of boilerplate to set up (which may be automated away eventually), and it +requires you to apply a contextual message to every underlying error that is +thrown inside your code. It can be a lot of work to maintain this pattern. + +Additionally, like the Error type, the Context type may use an allocation and a +dynamic dispatch internally. If you know this is too expensive for your use +case, you should not use this pattern. + +[use-error]: ./use-error.html +[custom-fail]: ./custom-fail.html +[context-api]: https://docs.rs/failure/latest/failure/struct.Context.html diff --git a/third_party/rust/failure/book/src/error-msg.md b/third_party/rust/failure/book/src/error-msg.md new file mode 100644 index 0000000000..61bde143b1 --- /dev/null +++ b/third_party/rust/failure/book/src/error-msg.md @@ -0,0 +1,59 @@ +# Strings as errors + +This pattern is a way to create new errors without doing much set up. It is +definitely the sloppiest way to throw errors. It can be great to use this +during prototyping, but maybe not in the final product. + +String types do not implement `Fail`, which is why there are two adapters to +create failures from a string: + +- [`failure::err_msg`][err-msg-api] - a function that takes a displayable + type and creates a failure from it. This can take a String or a string + literal. +- [`format_err!`][format-err-api] - a macro with string interpolation, similar + to `format!` or `println!`. + +```rust +fn check_range(x: usize, range: Range<usize>) -> Result<usize, Error> { + if x < range.start { + return Err(format_err!("{} is below {}", x, range.start)); + } + if x >= range.end { + return Err(format_err!("{} is above {}", x, range.end)); + } + Ok(x) +} +``` + +If you're going to use strings as errors, we recommend [using +`Error`][use-error] as your error type, rather than `ErrorMessage`; this way, +if some of your strings are `String` and some are `&'static str`, you don't +need worry about merging them into a single string type. + +## When might you use this pattern? + +This pattern is the easiest to set up and get going with, so it can be great +when prototyping or spiking out an early design. It can also be great when you +know that an error variant is extremely uncommon, and that there is really no +way to handle it other than to log the error and move on. + +## Caveats on this pattern + +If you are writing a library you plan to publish to crates.io, this is probably +not a good way to handle errors, because it doesn't give your clients very much +control. For public, open source libraries, we'd recommend using [custom +failures][custom-fail] in the cases where you would use a string as an error. + +This pattern can also be very brittle. If you ever want to branch over which +error was returned, you would have to match on the exact contents of the +string. If you ever change the string contents, that will silently break that +match. + +For these reasons, we strongly recommend against using this pattern except for +prototyping and when you know the error is just going to get logged or reported +to the users. + +[custom-fail]: ./custom-fail.html +[use-error]: ./use-error.html +[err-msg-api]: https://docs.rs/failure/latest/failure/fn.err_msg.html +[format-err-api]: https://docs.rs/failure/latest/failure/macro.format_err.html diff --git a/third_party/rust/failure/book/src/error.md b/third_party/rust/failure/book/src/error.md new file mode 100644 index 0000000000..f37e4c3fca --- /dev/null +++ b/third_party/rust/failure/book/src/error.md @@ -0,0 +1,100 @@ +# The `Error` type + +In addition to the trait `Fail`, failure provides a type called `Error`. Any +type that implements `Fail` can be cast into `Error` using From and Into, which +allows users to throw errors using `?` which have different types, if the +function returns an `Error`. + +For example: + +```rust +// Something you can deserialize +#[derive(Deserialize)] +struct Object { + ... +} + +impl Object { + // This throws both IO Errors and JSON Errors, but they both get converted + // into the Error type. + fn from_file(path: &Path) -> Result<Object, Error> { + let mut string = String::new(); + File::open(path)?.read_to_string(&mut string)?; + let object = json::from_str(&string)?; + Ok(object) + } +} +``` + +## Causes and Backtraces + +The Error type has all of the methods from the Fail trait, with a few notable +differences. Most importantly, the cause and backtrace methods on Error do not +return Options - an Error is *guaranteed* to have a cause and a backtrace. + +```rust +// Both methods are guaranteed to return an &Fail and an &Backtrace +println!("{}, {}", error.cause(), error.backtrace()) +``` + +An `Error`'s cause is always the failure that was cast into this `Error`. +That failure may have further underlying causes. Unlike Fail, this means that +the cause of an Error will have the same Display representation as the Error +itself. + +As to the error's guaranteed backtrace, when the conversion into the Error type +happens, if the underlying failure does not provide a backtrace, a new +backtrace is constructed pointing to that conversion point (rather than the +origin of the error). This construction only happens if there is no underlying +backtrace; if it does have a backtrace no new backtrace is constructed. + +## Downcasting + +The Error type also supports downcasting into any concrete Fail type. It can be +downcast by reference or by value - when downcasting by value, the return type +is `Result<T, Error>`, allowing you to get the error back out of it. + +```rust +match error.downcast::<io::Error>() { + Ok(io_error) => { ... } + Err(error) => { ... } +} +``` + +## Implementation details + +`Error` is essentially a trait object, but with some fanciness it may generate +and store the backtrace if the underlying failure did not have one. In +particular, we use a custom dynamically sized type to store the backtrace +information inline with the trait object data. + +```rust +struct Error { + // Inner<Fail> is a dynamically sized type + inner: Box<Inner<Fail>>, +} + +struct Inner<F: Fail> { + backtrace: Backtrace, + failure: F, +} +``` + +By storing the backtrace in the heap this way, we avoid increasing the size of +the Error type beyond that of two non-nullable pointers. This keeps the size of +the `Result` type from getting too large, avoiding having a negative impact on +the "happy path" of returning Ok. For example, a `Result<(), Error>` should be +represented as a pair of nullable pointers, with the null case representing +`Ok`. Similar optimizations can be applied to values up to at least a pointer +in size. + +To emphasize: Error is intended for use cases where the error case is +considered relatively uncommon. This optimization makes the overhead of an +error less than it otherwise would be for the Ok branch. In cases where errors +are going to be returned extremely frequently, returning this Error type is +probably not appropriate, but you should benchmark in those cases. + +(As a rule of thumb: if you're not sure if you can afford to have a trait +object, you probably *can* afford it. Heap allocations are not nearly as cheap +as stack allocations, but they're cheap enough that you can almost always +afford them.) diff --git a/third_party/rust/failure/book/src/fail.md b/third_party/rust/failure/book/src/fail.md new file mode 100644 index 0000000000..720b52e6d8 --- /dev/null +++ b/third_party/rust/failure/book/src/fail.md @@ -0,0 +1,152 @@ +# The `Fail` trait + +The `Fail` trait is a replacement for [`std::error::Error`][stderror]. It has +been designed to support a number of operations: + +- Because it is bound by both `Debug` and `Display`, any failure can be + printed in two ways. +- It has both a `backtrace` and a `cause` method, allowing users to get + information about how the error occurred. +- It supports wrapping failures in additional contextual information. +- Because it is bound by `Send` and `Sync`, failures can be moved and shared + between threads easily. +- Because it is bound by `'static`, the abstract `Fail` trait object can be + downcast into concrete types. + +Every new error type in your code should implement `Fail`, so it can be +integrated into the entire system built around this trait. You can manually +implement `Fail` yourself, or you can use the derive for `Fail` defined +in a separate crate and documented [here][derive-docs]. + +Implementors of this trait are called 'failures'. + +## Cause + +Often, an error type contains (or could contain) another underlying error type +which represents the "cause" of this error - for example, if your custom error +contains an `io::Error`, that is the cause of your error. + +The cause method on the `Fail` trait allows all errors to expose their underlying +cause - if they have one - in a consistent way. Users can loop over the chain +of causes, for example, getting the entire series of causes for an error: + +```rust +// Assume err is a type that implements `Fail` +let mut fail: &Fail = err; + +while let Some(cause) = fail.cause() { + println!("{}", cause); + + // Make `fail` the reference to the cause of the previous fail, making the + // loop "dig deeper" into the cause chain. + fail = cause; +} +``` + +Because `&Fail` supports downcasting, you can also inspect causes in more +detail if you are expecting a certain failure: + +```rust +while let Some(cause) = fail.cause() { + + if let Some(err) = cause.downcast_ref::<io::Error>() { + // treat io::Error specially + } else { + // fallback case + } + + fail = cause; +} +``` + +For convenience an iterator is also provided: + +```rust +// Assume err is a type that implements `Fail` +let mut fail: &Fail = err; + +for cause in fail.iter_causes() { + println!("{}", cause); +} +``` + +## Backtraces + +Errors can also generate a backtrace when they are constructed, helping you +determine the place the error was generated and the function chain that called into +that. Like causes, this is entirely optional - the authors of each failure +have to decide if generating a backtrace is appropriate in their use case. + +The backtrace method allows all errors to expose their backtrace if they have +one. This enables a consistent method for getting the backtrace from an error: + +```rust +// We don't even know the type of the cause, but we can still get its +// backtrace. +if let Some(bt) = err.cause().and_then(|cause| cause.backtrace()) { + println!("{}", bt) +} +``` + +The `Backtrace` type exposed by `failure` is different from the `Backtrace` exposed +by the [backtrace crate][backtrace-crate], in that it has several optimizations: + +- It has a `no_std` compatible form which will never be generated (because + backtraces require heap allocation), and should be entirely compiled out. +- It will not be generated unless the `RUST_BACKTRACE` environment variable has + been set at runtime. +- Symbol resolution is delayed until the backtrace is actually printed, because + this is the most expensive part of generating a backtrace. + +## Context + +Often, the libraries you are using will present error messages that don't +provide very helpful information about what exactly has gone wrong. For +example, if an `io::Error` says that an entity was "Not Found," that doesn't +communicate much about what specific file was missing - if it even was a file +(as opposed to a directory for example). + +You can inject additional context to be carried with this error value, +providing semantic information about the nature of the error appropriate to the +level of abstraction that the code you are writing operates at. The `context` +method on `Fail` takes any displayable value (such as a string) to act as +context for this error. + +Using the `ResultExt` trait, you can also get `context` as a convenient method on +`Result` directly. For example, suppose that your code attempted to read from a +Cargo.toml. You can wrap the `io::Error`s that occur with additional context +about what operation has failed: + +```rust +use failure::ResultExt; + +let mut file = File::open(cargo_toml_path).context("Missing Cargo.toml")?; +file.read_to_end(&buffer).context("Could not read Cargo.toml")?; +``` + +The `Context` object also has a constructor that does not take an underlying +error, allowing you to create ad hoc Context errors alongside those created by +applying the `context` method to an underlying error. + +## Backwards compatibility + +We've taken several steps to make transitioning from `std::error` to `failure` as +painless as possible. + +First, there is a blanket implementation of `Fail` for all types that implement +`std::error::Error`, as long as they are `Send + Sync + 'static`. If you are +dealing with a library that hasn't shifted to `Fail`, it is automatically +compatible with `failure` already. + +Second, `Fail` contains a method called `compat`, which produces a type that +implements `std::error::Error`. If you have a type that implements `Fail`, but +not the older `Error` trait, you can call `compat` to get a type that does +implement that trait (for example, if you need to return a `Box<Error>`). + +The biggest hole in our backwards compatibility story is that you cannot +implement `std::error::Error` and also override the backtrace and cause methods +on `Fail`. We intend to enable this with specialization when it becomes stable. + +[derive-docs]: ./derive-fail.html +[stderror]: https://doc.rust-lang.org/std/error/trait.Error.html +[backtrace-crate]: http://alexcrichton.com/backtrace-rs diff --git a/third_party/rust/failure/book/src/guidance.md b/third_party/rust/failure/book/src/guidance.md new file mode 100644 index 0000000000..7023ca40de --- /dev/null +++ b/third_party/rust/failure/book/src/guidance.md @@ -0,0 +1,24 @@ +# Patterns & Guidance + +failure is not a "one size fits all" approach to error management. There are +multiple patterns that emerge from the API this library provides, and users +need to determine which pattern makes sense for them. This section documents +some patterns and how users might use them. + +In brief, these are the patterns documented here: + +- **[Strings as errors](./error-msg.md):** Using strings as your error + type. Good for prototyping. +- **[A Custom Fail type](./custom-fail.md):** Defining a custom type to be + your error type. Good for APIs where you control all or more of the + possible failures. +- **[Using the Error type](./use-error.md):** Using the Error type to pull + together multiple failures of different types. Good for applications and + APIs that know the error won't be inspected much more. +- **[An Error and ErrorKind pair](./error-errorkind.md):** Using both a + custom error type and an ErrorKind enum to create a very robust error + type. Good for public APIs in large crates. + +(Though each of these items identifies a use case which this pattern would be +good for, in truth each of them can be applied in various contexts. Its up to +you to decide what makes the most sense for your particular use case.) diff --git a/third_party/rust/failure/book/src/howto.md b/third_party/rust/failure/book/src/howto.md new file mode 100644 index 0000000000..5c8135b7a5 --- /dev/null +++ b/third_party/rust/failure/book/src/howto.md @@ -0,0 +1,8 @@ +# How to use failure + +This section of the documentation is about how the APIs exposed in failure can +be used. It is organized around the major APIs of failure: + +- **[The Fail trait](./fail.md):** The primary abstraction provided by failure. +- **[Deriving Fail](./derive-fail.md):** A custom derive for the Fail trait. +- **[The Error type](./error.md):** A convenient wrapper around any Fail type. diff --git a/third_party/rust/failure/book/src/intro.md b/third_party/rust/failure/book/src/intro.md new file mode 100644 index 0000000000..318477a72d --- /dev/null +++ b/third_party/rust/failure/book/src/intro.md @@ -0,0 +1,77 @@ +# failure + +This is the documentation for the failure crate, which provides a system for +creating and managing errors in Rust. Additional documentation is found here: + +* [API documentation][api] +* [failure source code][repo] + +[api]: https://docs.rs/failure +[repo]: https://github.com/rust-lang-nursery/failure + +```rust +extern crate serde; +extern crate toml; + +#[macro_use] extern crate failure; +#[macro_use] extern crate serde_derive; + +use std::collections::HashMap; +use std::path::PathBuf; +use std::str::FromStr; + +use failure::Error; + +// This is a new error type that you've created. It represents the ways a +// toolchain could be invalid. +// +// The custom derive for Fail derives an impl of both Fail and Display. +// We don't do any other magic like creating new types. +#[derive(Debug, Fail)] +enum ToolchainError { + #[fail(display = "invalid toolchain name: {}", name)] + InvalidToolchainName { + name: String, + }, + #[fail(display = "unknown toolchain version: {}", version)] + UnknownToolchainVersion { + version: String, + } +} + +pub struct ToolchainId { + // ... etc +} + +impl FromStr for ToolchainId { + type Err = ToolchainError; + + fn from_str(s: &str) -> Result<ToolchainId, ToolchainError> { + // ... etc + } +} + +pub type Toolchains = HashMap<ToolchainId, PathBuf>; + +// This opens a toml file containing associations between ToolchainIds and +// Paths (the roots of those toolchains). +// +// This could encounter an io Error, a toml parsing error, or a ToolchainError, +// all of them will be thrown into the special Error type +pub fn read_toolchains(path: PathBuf) -> Result<Toolchains, Error> +{ + use std::fs::File; + use std::io::Read; + + let mut string = String::new(); + File::open(path)?.read_to_string(&mut string)?; + + let toml: HashMap<String, PathBuf> = toml::from_str(&string)?; + + let toolchains = toml.iter().map(|(key, path)| { + let toolchain_id = key.parse()?; + Ok((toolchain_id, path)) + }).collect::<Result<Toolchains, ToolchainError>>()?; + + Ok(toolchains) +} diff --git a/third_party/rust/failure/book/src/string-custom-error.md b/third_party/rust/failure/book/src/string-custom-error.md new file mode 100644 index 0000000000..5ea7b8fc82 --- /dev/null +++ b/third_party/rust/failure/book/src/string-custom-error.md @@ -0,0 +1,168 @@ +# Strings and custom fail type + +This pattern is an hybrid between the [_An Error and ErrorKind pair_](./error-errorkind.md) and +[_Using the Error type_](./use-error.md). + +Such an error type can be implemented in the same way that what was shown in +the [_An Error and ErrorKind pair_](./error-errorkind.md) pattern, but here, the context is a +simple string: + +```rust +extern crate core; +extern crate failure; + +use core::fmt::{self, Display}; +use failure::{Backtrace, Context, Fail, ResultExt}; + +#[derive(Debug)] +pub struct MyError { + inner: Context<String>, +} + +impl Fail for MyError { + fn name(&self) -> Option<&str> { + self.inner.name() + } + + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl Display for MyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} +``` + +To make the type easier to use, a few impls can be added: + +```rust +// Allows writing `MyError::from("oops"))?` +impl From<&'static str> for MyError { + fn from(msg: &'static str) -> MyError { + MyError { + inner: Context::new(msg), + } + } +} + +// Allows adding more context via a String +impl From<Context<String>> for MyError { + fn from(inner: Context<String>) -> MyError { + MyError { inner } + } +} + +// Allows adding more context via a &str +impl From<Context<&'static str>> for MyError { + fn from(inner: Context<&'static str>) -> MyError { + MyError { + inner: inner.map(|s| s.to_string()), + } + } +} +``` + +Here is how it is used: + +```rust +fn main() { + println!("{:?}", err2()); +} + +// Unlike the "Using the Error type" pattern, functions return our own error +// type here. +fn err1() -> Result<(), MyError> { + Ok(Err(MyError::from("err1"))?) +} + +fn err2() -> Result<(), MyError> { + // Unlike the "An Error and ErrorKind pair" pattern, our context is a + // simple string. We can chain errors and provide detailed error messages, + // but we don't have to deal with the complexity of an error kind type + Ok(err1().context("err2")?) +} +``` + +## Variant with `&'static str` + +If you don't need to format strings, you can avoid an +allocation by using a `Context<&'static str>` instead of a +`Context<String>`. + +```rust +extern crate core; +extern crate failure; + +use core::fmt::{self, Display}; +use failure::{Backtrace, Context, Fail, ResultExt}; + +#[derive(Debug)] +pub struct MyError { + inner: Context<&'static str>, +} + +impl Fail for MyError { + fn name(&self) -> Option<&str> { + self.inner.name() + } + + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl Display for MyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl From<&'static str> for MyError { + fn from(msg: &'static str) -> MyError { + MyError { + inner: Context::new(msg.into()), + } + } +} + +impl From<Context<&'static str>> for MyError { + fn from(inner: Context<&'static str>) -> MyError { + MyError { + inner, + } + } +} +``` + +## When might you use this pattern? + +Sometimes, you don't want to use the [_Using the Error type_](./use-error.md) +pattern, because you want to expose a few different error types. But you don't +want to use the [_An Error and ErrorKind pair_](./error-errorkind.md) pattern +either, because there is no need to provide the context as an enum or because +it would be too much work, if the error can occur in many different contexts. + +For instance, if you're writing a library that decodes/encodes a complex binary +format, you might want to expose a `DecodeError` and an `EncodeError` error +type, but provide the context as a simple string instead of an error kind, because: + +- users may not care too much about the context in which a `DecodeError` or + `EncodeError` was encountered, they just want a nice message to explain it +- your binary format is really complex, errors can occur in many different + places, and you don't want to end up with a giant `ErrorKind` enum + + +## Caveats on this pattern + +If using the `Context<String>` variant, an extra allocation is used for the string. diff --git a/third_party/rust/failure/book/src/use-error.md b/third_party/rust/failure/book/src/use-error.md new file mode 100644 index 0000000000..a0a2944022 --- /dev/null +++ b/third_party/rust/failure/book/src/use-error.md @@ -0,0 +1,66 @@ +# Use the `Error` type + +This pattern is a way to manage errors when you have multiple kinds of failure +that could occur during a single function. It has several distinct advantages: + +1. You can start using it without defining any of your own failure types. +2. All types that implement `Fail` can be thrown into the `Error` type using +the `?` operator. +3. As you start adding new dependencies with their own failure types, you can +start throwing them without making a breaking change. + +To use this pattern, all you need to do is return `Result<_, Error>` from your +functions: + +```rust +use std::io; +use std::io::BufRead; + +use failure::Error; +use failure::err_msg; + +fn my_function() -> Result<(), Error> { + let stdin = io::stdin(); + + for line in stdin.lock().lines() { + let line = line?; + + if line.chars().all(|c| c.is_whitespace()) { + break + } + + if !line.starts_with("$") { + return Err(format_err!("Input did not begin with `$`")); + } + + println!("{}", &line[1..]); + } + + Ok(()) +} +``` + +## When might you use this pattern? + +This pattern is very effective when you know you will usually not need to +destructure the error this function returns. For example: + +- When prototyping. +- When you know you are going to log this error, or display it to the user, + either all of the time or nearly all of the time. +- When it would be impractical for this API to report more custom context for + the error (e.g. because it is a trait that doesn't want to add a new Error + associated type). + +## Caveats on this pattern + +There are two primary downsides to this pattern: + +- The `Error` type allocates. There are cases where this would be too + expensive. In those cases you should use a [custom failure][custom-fail]. +- You cannot recover more information about this error without downcasting. If + your API needs to express more contextual information about the error, use + the [Error and ErrorKind][error-errorkind] pattern. + +[custom-fail]: ./custom-fail.html +[error-errorkind]: ./error-errorkind.html |