A macro for defining error types.
Introduction
The define_errors
macro defines new error types. The error types
are enums that can hold other error values and/or error
descriptions. The macro is designed to allow the adding of
multiple levels of context to errors by sucessively wrapping
them, without losing any of the structured information contained
in the wrapped errors. It tries to be as simple as possible while
still achieving that goal.
Plain and wrapped errors
To illustrate the syntax we will use examples. Firstly, the
following code will define a new error type called MyError
, and
a macro myerr_new
that can be used to create new values of type
MyError
:
# extern crate define_errors;
use Error; // required use statement
define_errors!
#
Note that the use
statement is required. However, you can use
another name binding if you need to. For example, this will also
work:
# extern crate define_errors;
use Error as StdError;
define_errors!
#
If the type being defined needs to be declared public you can add
the pub
keyword before the name. Also, you can define multiple
error types at once (although in most cases one is all that is required):
# extern crate define_errors;
# use Error;
define_errors!
#
The MyError
definition inserted by the macro above looks like
this:
Since MyError
is an enum, MyError
values can be different
variants. In this case: Plain
and Wrapped
. Plain
holds a
simple description string, but Wrapped
holds a description and a
"wrapped" error, also of type MyError
. The two variants can be
created in a simple way using the automatically defined
myerr_new
macro, as shown in the next code example. The
description passed to myerr_new
can be either a &str
or a
String
:
# extern crate define_errors;
# use Error;
# define_errors!
#
When e_wrapped
is converted to a string it will show the
description given to the myerr_new
call, as well as the
description contained inside the wrapped error:
# extern crate define_errors;
# use Error;
# define_errors!
#
In other words, e_wrapped
has taken e_plain
and added context
to it. It should also be noted that e_wrapped
takes ownership of
e_plain
.
Formatted descriptions
There is also a shortcut for creating formatted descriptions for
Plain
and Wrapped
. Instead of
# extern crate define_errors;
# use Error;
# define_errors!
#
you can instead use
# extern crate define_errors;
# use Error;
# define_errors!
#
This works for up to six format
arguments, and for Plain
and
Wrapped
variants of MyError
.
Existing error types
define_errors
can also define error types that holds existing
error types (such as std::io::Error
) in corresponding
variants. For example:
# extern crate define_errors;
# use Error;
define_errors!
#
This defines an enum that looks like:
Plain
and Wrapped
variants are still present, but we now also
have Io
and Utf8
. These new variants of MyError
can be
created in a similar fashion to before, again using
myerr_new
. As when creating Wrapped
variants, myerr_new
takes ownership of the errors being passed to it:
# extern crate define_errors;
# use Error;
# use ErrorKind;
# define_errors!
#
A nice thing is that the From
traits for existing error types
are implemented as standard by define_errors
. In this case:
From<std::io::Error> for MyError
From<std::str::Utf8Error> for MyError
This means that when using the question mark operator or try
macro, existing error types get converted to MyError
automatically:
# extern crate define_errors;
# use Error;
# define_errors!
type MyResult<T> = ;
Lastly, MyError
also implements the std::error::Error
trait,
so you can create trait objects if you wish:
# extern crate define_errors;
# use Error;
# define_errors!
That is all there is to define_errors
.
A complete example
Here is a complete example illustrating most of the above:
extern crate define_errors;
use Error;
use File;
use Read;
use Path;
define_errors!
type MyResult<T> = ;