mirror of
https://github.com/haraldh/chainerror.git
synced 2025-05-31 22:08:07 +02:00
lots of documentation
This commit is contained in:
parent
5145b950b5
commit
8ad6eaceac
24 changed files with 892 additions and 89 deletions
1
booksrc/README.md
Symbolic link
1
booksrc/README.md
Symbolic link
|
@ -0,0 +1 @@
|
|||
../README.md
|
|
@ -1,13 +1,17 @@
|
|||
# Summary
|
||||
|
||||
- [Chapter 1](./tutorial1.md)
|
||||
- [Chapter 2](./tutorial2.md)
|
||||
- [Chapter 3](./tutorial3.md)
|
||||
- [Chapter 4](./tutorial4.md)
|
||||
- [Chapter 5](./tutorial5.md)
|
||||
- [Chapter 6](./tutorial6.md)
|
||||
- [Chapter 7](./tutorial7.md)
|
||||
- [Chapter 8](./tutorial8.md)
|
||||
- [Chapter 9](./tutorial9.md)
|
||||
- [Chapter 10](./tutorial10.md)
|
||||
- [Chapter 11](./tutorial11.md)
|
||||
[chainerror](README.md)
|
||||
|
||||
- [Simple String Errors](tutorial1.md)
|
||||
- [Simple Chained String Errors](tutorial2.md)
|
||||
- [Mapping Errors](tutorial3.md)
|
||||
- [Saving coding chars](tutorial4.md)
|
||||
- [The source() of Errors](tutorial5.md)
|
||||
- [Downcast the Errors](tutorial6.md)
|
||||
- [The root cause of all Errors](tutorial7.md)
|
||||
- [Finding an Error cause](tutorial8.md)
|
||||
- [Selective Error Handling](tutorial9.md)
|
||||
- [ErrorKind to the rescue](tutorial10.md)
|
||||
- [Debug for the ErrorKind](tutorial11.md)
|
||||
|
||||
[The End](end.md)
|
5
booksrc/end.md
Normal file
5
booksrc/end.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# The End
|
||||
|
||||
That's it for now…
|
||||
|
||||
Happy error handling!
|
|
@ -1,12 +1,26 @@
|
|||
# Simple String Errors
|
||||
|
||||
The most simplest of doing error handling in rust is by returning `String` as a `Box<Error>`.
|
||||
An easy way of doing error handling in rust is by returning `String` as a `Box<std::error::Error>`.
|
||||
|
||||
As you can see by running the example (the "Play" button in upper right of the code block), this only
|
||||
If the rust `main` function returns an `Err()`, this `Err()` will be displayed with `std::fmt::Debug`.
|
||||
|
||||
As you can see by running the example (by pressing the "Play" button in upper right of the code block),
|
||||
this only
|
||||
prints out the last `Error`.
|
||||
|
||||
If the rust `main` function returns an Err(), this Err() will be displayed with `std::fmt::Debug`.
|
||||
~~~
|
||||
Error: StringError("func1 error")
|
||||
~~~
|
||||
|
||||
The next chapters of this tutorial show how `chainerror` adds more information
|
||||
and improves inspecting the sources of an error.
|
||||
|
||||
You can also run the tutorial examples in the checked out
|
||||
[chainerror git repo](https://github.com/haraldh/chainerror).
|
||||
~~~
|
||||
$ cargo run -q --example tutorial1
|
||||
~~~
|
||||
|
||||
~~~rust
|
||||
{{#include ../examples/tutorial1.rs:2:}}
|
||||
{{#include ../examples/tutorial1.rs}}
|
||||
~~~
|
|
@ -1,6 +1,20 @@
|
|||
# ErrorKind to the rescue
|
||||
|
||||
[TBD]
|
||||
To cope with different kind of errors, we introduce the kind of an error `Func1ErrorKind` with an enum.
|
||||
|
||||
Because we derive `Debug` and implement `Display` our `Func1ErrorKind` enum, this enum can be used as
|
||||
a `std::error::Error`.
|
||||
|
||||
Not using `String` errors anymore, the `cherr!()` macro seen in the beginning of
|
||||
the tutorial has to be used again.
|
||||
|
||||
Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box<Error>>` and we can
|
||||
use `ChainResult<(), Func1ErrorKind>`.
|
||||
|
||||
In `main` we can now directly use the methods of `ChainError<T>` without downcasting the error first.
|
||||
|
||||
Also a nice `match` on `ChainError<T>.kind()` is now possible, which returns `&T`, meaning
|
||||
`&Func1ErrorKind` here.
|
||||
|
||||
~~~rust
|
||||
use crate::chainerror::*;
|
||||
|
|
|
@ -1,6 +1,23 @@
|
|||
# Debug for the ErrorKind
|
||||
|
||||
[TBD]
|
||||
One small improvement at the end of the tutorial is to fix the debug output of
|
||||
`Func1ErrorKind`. As you probably noticed, the output doesn't say much of the enum.
|
||||
|
||||
~~~
|
||||
Debug Error:
|
||||
src/main.rs:35: Func2
|
||||
[…]
|
||||
~~~
|
||||
|
||||
As a lazy shortcut, we implement `Debug` by calling `Display` and end up with
|
||||
|
||||
~~~
|
||||
Debug Error:
|
||||
src/main.rs:40: func1 error calling func2
|
||||
[…}
|
||||
~~~
|
||||
|
||||
which gives us a lot more detail.
|
||||
|
||||
~~~rust
|
||||
use crate::chainerror::*;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Simple Chained String Errors
|
||||
|
||||
Now with the help of the `chainerror` crate, we can have a nicer output.
|
||||
With relatively small changes and the help of the `cherr!` macro of the `chainerror` crate
|
||||
the `String` errors are now chained together.
|
||||
|
||||
Press the play button in the upper right corner and see the nice debug output.
|
||||
|
||||
|
@ -16,16 +17,17 @@ use crate::chainerror::*;
|
|||
### What did we do here?
|
||||
|
||||
~~~rust,ignore
|
||||
{{#include ../examples/tutorial2.rs:11:13}}
|
||||
{{#include ../examples/tutorial2.rs:13:15}}
|
||||
~~~
|
||||
|
||||
The macro `cherr!(cause, newerror)` stores `cause` as the source/cause of `newerror` and returns
|
||||
`newerror`, along with the filename (`file!()`) and line number (`line!()`).
|
||||
The macro `cherr!(olderror, newerror)` stores `olderror` as the source/cause of `newerror`
|
||||
along with the filename (`file!()`) and line number (`line!()`)
|
||||
and returns `newerror`.
|
||||
|
||||
`Err(e)?` then returns the error `e` applying `e.into()`, so that we
|
||||
`Err()?` then returns the inner error applying `.into()`, so that we
|
||||
again have a `Err(Box<Error>)` as a result.
|
||||
|
||||
The `Debug` implementation of `ChainError<T>` (which is returned by `cherr!()`)
|
||||
prints the `Debug` of `T` prefixed with the stored filename and line number.
|
||||
|
||||
`ChainError<T>` is in our case `ChainError<String>`.
|
||||
`ChainError<T>` in our case is `ChainError<String>`.
|
|
@ -12,8 +12,8 @@ use crate::chainerror::*;
|
|||
# }
|
||||
~~~
|
||||
|
||||
Note, that we changed the output of the error in `main()` from `Debug` to `Display`, so we don't see
|
||||
the error backtrace with filename and line number.
|
||||
Note, that because we changed the output of the error in `main()` from
|
||||
`Debug` to `Display`, we don't see the error backtrace with filename and line number.
|
||||
|
||||
To enable the `Display` backtrace, you have to enable the feature `display-cause` for `chainerror`.
|
||||
|
|
@ -1,6 +1,14 @@
|
|||
# Downcast the Errors
|
||||
|
||||
[TBD]
|
||||
`std::error::Error` comes with some helper methods to get to the original object of the
|
||||
`&(dyn Error + 'static)` returned by `.source()`.
|
||||
|
||||
~~~rust,ignore
|
||||
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T>
|
||||
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T>
|
||||
~~~
|
||||
|
||||
This is how it looks like, when using those:
|
||||
|
||||
~~~rust
|
||||
use crate::chainerror::*;
|
||||
|
|
|
@ -1,6 +1,30 @@
|
|||
# The root cause of all Errors
|
||||
|
||||
[TBD]
|
||||
`chainerror` also has some helper methods:
|
||||
|
||||
~~~rust,ignore
|
||||
fn is_chain<T: 'static + Display + Debug>(&self) -> bool
|
||||
fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>
|
||||
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>
|
||||
fn root_cause(&self) -> Option<&(dyn Error + 'static)>
|
||||
fn find_cause<U: Error + 'static>(&self) -> Option<&U>
|
||||
fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>>
|
||||
fn kind<'a>(&'a self) -> &'a T
|
||||
~~~
|
||||
|
||||
Using `downcast_chain_ref::<String>()` gives a `ChainError<String>`, which can be used
|
||||
to call `.find_cause::<io::Error>()`.
|
||||
|
||||
~~~rust,ignore
|
||||
if let Some(s) = e.downcast_chain_ref::<String>() {
|
||||
if let Some(ioerror) = s.find_cause::<io::Error>() {
|
||||
~~~
|
||||
|
||||
or to use `.root_cause()`, which of course can be of any type implementing `std::error::Error`.
|
||||
|
||||
~~~rust,ignore
|
||||
if let Some(e) = s.root_cause() {
|
||||
~~~
|
||||
|
||||
~~~rust
|
||||
use crate::chainerror::*;
|
||||
|
|
|
@ -1,6 +1,26 @@
|
|||
# Finding an Error cause
|
||||
|
||||
[TBD]
|
||||
To distinguish the errors occuring in various places, we can define named string errors with the
|
||||
"new type" pattern.
|
||||
|
||||
~~~rust,ignore
|
||||
derive_str_cherr!(Func2Error);
|
||||
derive_str_cherr!(Func1Error);
|
||||
~~~
|
||||
|
||||
Instead of `ChainError<String>` we now have `struct Func1Error(String)` and `ChainError<Func1Error>`.
|
||||
|
||||
In the `main` function you can see, how we can match the different errors.
|
||||
|
||||
Also see:
|
||||
~~~rust,ignore
|
||||
if let Some(f2err) = f1err.find_chain_cause::<Func2Error>() {
|
||||
~~~
|
||||
as a shortcut to
|
||||
~~~rust,ignore
|
||||
if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() {
|
||||
~~~
|
||||
hiding the `ChainError<T>` implementation detail.
|
||||
|
||||
~~~rust
|
||||
use crate::chainerror::*;
|
||||
|
|
|
@ -1,6 +1,28 @@
|
|||
# Selective Error Handling
|
||||
|
||||
[TBD]
|
||||
What about functions returning different Error types?
|
||||
|
||||
In this example `func1()` can return either `Func1ErrorFunc2` or `Func1ErrorIO`.
|
||||
|
||||
We might want to `match` on `func1()` with something like:
|
||||
|
||||
~~~rust,ignore
|
||||
fn main() -> Result<(), Box<Error>> {
|
||||
match func1() {
|
||||
Err(e) if let Some(s) = e.downcast_chain_ref::<Func1ErrorIO>() =>
|
||||
eprintln!("Func1ErrorIO:\n{:?}", s),
|
||||
|
||||
Err(e) if let Some(s) = e.downcast_chain_ref::<Func1ErrorFunc2>() =>
|
||||
eprintln!("Func1ErrorFunc2:\n{:?}", s),
|
||||
|
||||
Ok(_) => {},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
~~~
|
||||
|
||||
but this is not valid rust code, so we end up doing it the hard way.
|
||||
In the next chapter, we will see, how to solve this more elegantly.
|
||||
|
||||
~~~rust
|
||||
use crate::chainerror::*;
|
||||
|
@ -9,4 +31,4 @@ use crate::chainerror::*;
|
|||
# mod chainerror {
|
||||
{{#includecomment ../src/lib.rs}}
|
||||
# }
|
||||
~~~
|
||||
~~~
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue