Expand description
A Macros 1.1 implementation of https://crates.io/crates/error-chain
The error-chain example
mod other_error {
error_chain! {}
}
error_chain! {
types {
Error, ErrorKind, ResultExt, Result;
}
links {
Another(other_error::Error, other_error::ErrorKind) #[cfg(unix)];
}
foreign_links {
Fmt(::std::fmt::Error);
Io(::std::io::Error) #[cfg(unix)];
}
errors {
InvalidToolchainName(t: String) {
description("invalid toolchain name")
display("invalid toolchain name: '{}'", t)
}
}
}becomes
mod other_error {
#[derive(Debug, ErrorChain)]
pub enum ErrorKind {
Msg(String),
}
}
#[derive(Debug, ErrorChain)]
pub enum ErrorKind {
Msg(String),
#[cfg(unix)]
#[error_chain(link = "other_error::Error")]
Another(other_error::ErrorKind),
#[error_chain(foreign)]
Fmt(::std::fmt::Error),
#[cfg(unix)]
#[error_chain(foreign)]
Io(::std::io::Error),
#[error_chain(custom)]
#[error_chain(description = r#"|_| "invalid toolchain name""#)]
#[error_chain(display = r#"|t| write!(f, "invalid toolchain name: '{}'", t)"#)]
InvalidToolchainName(String),
}So the obvious differences from error_chain! are:
- The
ErrorKindis an enum instead of a macro invocation. - Error links are variants of the enum instead of lines inside the macro.
- Links have explicit annotations marking them as chainable / foreign / custom instead of being grouped into corresponding sections of the macro.
- Attributes like
#[cfg]are applied to the variants directly instead of needing special syntax. descriptionanddisplayare defined as function expressions specified as attribute values, instead of shorthands integrated into the macro syntax.
The less obvious differences are:
- The
ErrorKindmust explicitly implement::std::fmt::Debug, either automatically using#[derive]or manually implemented separately.error_chain!does this implicitly. - Unlike
error_chain!, theErrorKindneed not havepubvisibility. The generatedError,ResultandResultExtwill have the same visibility as theErrorKind. - The
ErrorKindcan have a specialMsg(String)member for converting strings to theErrorKind.error_chain!does this implicitly. - Unlike
error-chain, theMsg(String)member is optional. If absent, theErrorKindandErrorwill not implFrom<String>andFrom<&str>. - Doc comments, since they’re effectively attributes, can be applied on the enum variants without any special syntax like
error_chain!has. - The
ErrorKindcan be generic.
§Enum attributes
-
#[error_chain(error = "ErrorName")]Override the name of the generated
Errorstruct to the given name. If not provided, the struct will be namedError. -
#[error_chain(result_ext = "ResultExtName")]Override the name of the generated
ResultExttrait to the given name. If not provided, the trait will be namedResultExt. -
#[error_chain(result = "ResultName")]Override the name of the generated
Resulttype alias to the given name. If not provided, the alias will be namedResult. If set to the empty string"", the alias will not be generated at all. -
#[error_chain(backtrace = "false")]or#[error_chain(backtrace = false)]Disable backtrace functionality in the generated code. This should be kept in sync with the value of the
backtracefeature of theerror-chaincrate. In other words, if you setbacktrace = "false"here, you must also specifydefault-features = falseforerror-chainin yourCargo.toml
§Variant definitions
-
Chainable links
#[error_chain(link = "other_error::Error")] Another(other_error::ErrorKind),A chainable link is an error and errorkind that have been generated using
error-chainorderive-error-chain. The variant must have a single field to hold the chained errorkind, and thelinkattribute must specify a path to the chained error.When the
proc_macrofeature is enabled, the value of thelinkattribute does not need to be stringified:#[error_chain(link = other_error::Error)] Another(other_error::ErrorKind), -
Foreign links
#[error_chain(foreign)] Fmt(::std::fmt::Error),A foreign link is an error that implements
::std::error::Errorbut otherwise does not followerror-chain’s conventions. The variant must have a single field to hold the foreign error. -
Custom links
#[error_chain(custom)] InvalidToolchainName(String),A custom link is an arbitrary variant that can hold any members.
§Variant attributes
In addition to the above attributes that identify the type of the variant’s link, the below attributes can be used on all links.
-
#[error_chain(description = "some_function_expression")]Specifies a function expression to be used to implement
ErrorKind::description(). This value is also returned from the implementation of::std::error::Error::description()on the generatedError.This can be an inline lambda:
#[error_chain(description = r#"|_| "invalid toolchain name""#)] InvalidToolchainName(String),or it can be a separate function:
#[error_chain(description = "invalid_toolchain_name_error_description")] InvalidToolchainName(String), // <snip> fn invalid_toolchain_name_error_description(_: &str) -> &str { "invalid toolchain name" }The function expression must have the signature
(...) -> &'static str. It should have one parameter for each field of the variant. The fields are passed in by reference.Thus in the above example, since
InvalidToolchainNamehad a single field of typeString, the function expression needed to be of type(&str) -> &'static strIf not specified, the default implementation behaves in this way:
- Chainable links: Forwards to the chained error kind’s
description() - Foreign links: Forwards to the foreign error’s implementation of
::std::error::Error::description() - Custom links: Returns the stringified name of the variant.
When the
proc_macrofeature is enabled, the value does not need to be stringified:#[error_chain(description = |_| "invalid toolchain name")] InvalidToolchainName(String),#[error_chain(description = invalid_toolchain_name_error_description)] InvalidToolchainName(String),When the
proc_macrofeature is enabled, closure expressions that only callwrite!on the::std::fmt::Formattercan instead use a shorthand:#[error_chain(description = const("invalid toolchain name"))] InvalidToolchainName(String), - Chainable links: Forwards to the chained error kind’s
-
#[error_chain(display = "some_function_expression")]Specifies a function expression to be used to implement
::std::fmt::Display::fmt()on theErrorKindand generatedErrorThis can be an inline lambda:
#[error_chain(display = r#"|t| write!(f, "invalid toolchain name: '{}'", t)"#)] InvalidToolchainName(String),or it can be a separate function:
#[error_chain(display = "invalid_toolchain_name_error_display")] InvalidToolchainName(String), // <snip> fn invalid_toolchain_name_error_display(f: &mut ::std::fmt::Formatter, t: &str) -> ::std::fmt::Result { write!(f, "invalid toolchain name: '{}'", t) }The function expression must have the signature
(&mut ::std::fmt::Formatter, ...) -> ::std::fmt::Result. It should have one&mut ::std::fmt::Formatterparameter, and one parameter for each field of the variant. The fields are passed in by reference. For brevity, closure expressions do not need the&mut ::std::fmt::Formatterparameter and instead captureffrom the closure environment.Thus in the above example, since
InvalidToolchainNamehad a single field of typeString, the function expression needed to be of type(&mut ::std::fmt::Formatter, &str) -> ::std::fmt::ResultIf not specified, the default implementation of
::std::fmt::Display::fmt()behaves in this way:- Chainable links: Forwards to the chained errorkind’s implementation of
::std::fmt::Display::fmt() - Foreign links: Forwards to the foreign error’s implementation of
::std::fmt::Display::fmt() - Custom links: Writes the description of the variant to the formatter.
When the
proc_macrofeature is enabled, the value does not need to be stringified:#[error_chain(display = |t| write!(f, "invalid toolchain name: '{}'", t))] InvalidToolchainName(String),#[error_chain(display = invalid_toolchain_name_error_display)] InvalidToolchainName(String),When the
proc_macrofeature is enabled, closure expressions that only callwrite!on the::std::fmt::Formattercan instead use a shorthand:// Tuple variants use `{0}`, `{1}`, and so on #[error_chain(display = const("invalid toolchain name: '{0}'"))] InvalidToolchainName(String),// Struct variants use `{name_of_the_field}` #[error_chain(display = const("invalid toolchain name: '{name}'"))] InvalidToolchainName { name: String }, - Chainable links: Forwards to the chained errorkind’s implementation of
-
#[error_chain(cause = "some_function_expression")]Specifies a function expression to be used to implement
::std::fmt::Error::cause()on the generatedErrorThis can be an inline lambda:
#[error_chain(cause = "|_, err| err")] Io(::std::path::PathBuf, ::std::io::Error),or it can be a separate function:
#[error_chain(cause = "parse_file_error_cause")] Io(::std::path::PathBuf, ::std::io::Error), // <snip> fn parse_file_error_cause<'a>(_: &::std::path::Path, err: &'a ::std::io::Error) -> &'a ::std::error::Error { err }The function expression must have the signature
(...) -> &::std::error::Error. It should have one parameter for each field of the variant. The fields are passed in by reference. The result is wrapped inOption::Some()for returning from::std::error::Error::cause()Thus in the above example, since
Iohad two fields of type::std::path::PathBufand::std::io::Error, the function expression needed to be of type(&::std::path::Path, &::std::io::Error) -> &::std::error::ErrorIf not specified, the default implementation of
::std::error::Error::cause()behaves in this way:- Chainable links: Returns
None - Foreign links: Forwards to the foreign error’s implementation of
::std::error::Error::cause() - Custom links: Returns
None
When the
proc_macrofeature is enabled, the value does not need to be stringified:#[error_chain(cause = |_, err| err)] Io(::std::path::PathBuf, ::std::io::Error),#[error_chain(cause = parse_file_error_cause)] Io(::std::path::PathBuf, ::std::io::Error), - Chainable links: Returns
§Conflicts with error-chain macros when the proc_macro feature is enabled
If you have the proc_macro feature enabled and have code like this:
#![feature(proc_macro)]
#[macro_use] extern crate derive_error_chain;
#[macro_use] extern crate error_chain; // Want to use `bail!` and `quick_main!`
#[derive(Debug, ErrorChain)]
#[error_chain(result = "MyResult")]
enum ErrorKind {
Msg(String),
}
quick_main!(|| -> MyResult<()> {
bail!("failed");
});it’ll fail to compile with:
error: macro `error_chain` may not be used in attributesThis is because the compiler thinks #[error_chain(result = "MyResult")] is the invocation of an attribute macro, notices that error_chain! is
a macro_rules macro brought into scope from the error-chain crate, and thus complains that a macro_rules macro cannot be used as
an attribute macro. It does this even though there is no attribute macro named error_chain and that the custom derive from this crate
has registered error_chain as an attribute it supports.
See https://github.com/rust-lang/rust/issues/38356#issuecomment-324277403 for the discussion.
To work around this, don’t use #[macro_use] with the error-chain crate. Instead, either use the macros you need from it:
#![feature(proc_macro)]
#[macro_use] extern crate derive_error_chain;
extern crate error_chain;
use error_chain::{ bail, quick_main };
#[derive(Debug, ErrorChain)]
#[error_chain(result = "MyResult")]
enum ErrorKind {
Msg(String),
}
quick_main!(|| -> MyResult<()> {
bail!("failed");
});or fully qualify their paths:
#![feature(proc_macro)]
#[macro_use] extern crate derive_error_chain;
extern crate error_chain;
#[derive(Debug, ErrorChain)]
#[error_chain(result = "MyResult")]
enum ErrorKind {
Msg(String),
}
error_chain::quick_main!(|| -> MyResult<()> {
error_chain::bail!("failed");
});useing the error_chain! macro itself is more complicated: it must be renamed so that it doesn’t just cause the above error again,
and other macros it uses must also be imported, even though they’re an implementation detail:
#![feature(proc_macro)]
#[macro_use] extern crate derive_error_chain;
extern crate error_chain;
use error_chain::{ error_chain as error_chain_macro, error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace };
#[derive(Debug, ErrorChain)]
#[error_chain(error = "MyError", result = "MyResult", result_ext = "MyResultExt")]
enum MyErrorKind {
Msg(String),
}
error_chain_macro! {
}To use it fully-qualified, the macros it depends on must still be used to bring them into scope:
#![feature(proc_macro)]
#[macro_use] extern crate derive_error_chain;
extern crate error_chain;
use error_chain::{ error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace };
#[derive(Debug, ErrorChain)]
#[error_chain(error = "MyError", result = "MyResult", result_ext = "MyResultExt")]
enum MyErrorKind {
Msg(String),
}
error_chain::error_chain! {
}It’s possible this experience will be made better before the proc_macro feature stabilizes.