coercible_errors/
lib.rs

1//! Zero-cost error handling for generic traits.
2//!
3//! # Rationale
4//!
5//! Assume you want to build a crate that defines a generic trait,
6//! meant to be implemented by others.
7//! Some implementations of that trait will always succeed,
8//! others may sometimes fail.
9//! The methods of the generic trait should therefore return `Result<_,_>`,
10//! but that should not induce an overhead for infallible implementations
11//! (per the *zero-cost abstraction* motto).
12//!
13//! The [`coercible_errors!`] macro will define a set of traits and types
14//! that you can use to define your generic traits,
15//! in order to keep their error-handling as efficient as possible.
16//! More precisely, the compiler will be able to optimize away the error types
17//! whenever only infallible implementations of the generic trait are used.
18//!
19//! See `README.md` for a more detailed explaination.
20//!
21//! [`coercible_errors!`]: macro.coercible_errors.html
22
23/// Sets up coercible_errors for a previously defined error type.
24///
25/// It re-exports the types [`Never`] and [`OkResult`],
26/// and defines three new traits and types `CoercibleWith`,
27/// `CoercedError` and `CoercedResult`.
28///
29/// [`Never`]: enum.Never.html
30/// [`OkResult`]: type.OkResult.html
31#[macro_export]
32macro_rules! coercible_errors {
33    () => {
34        coercible_errors!(Error);
35    };
36    ($error: ty) => {
37        coercible_errors!($error, CoercibleWith, CoercedError, CoercedResult);
38    };
39    ($error: ty, $coercible_with: ident, $coerced_error: ident, $coerced_result: ident) => {
40        pub use $crate::{Never, OkResult};
41
42        // This conversion can never happen (since Never can have no value),
43        // but it is required for allowing $error and Never to coerce with each other.
44        impl From<Never> for $error {
45            fn from(_: Never) -> $error {
46                unreachable!()
47            }
48        }
49
50        /// A trait used to determine how to best coerce two error types.
51        ///
52        /// In practice, the only two error types that it handles are `$error` or `Never`.
53        pub trait $coercible_with<E>:
54            Sized + std::marker::Send + std::error::Error + 'static
55        {
56            type Into: std::marker::Send
57                + std::error::Error
58                + 'static
59                + From<Self>
60                + From<E>
61                + $coercible_with<$error>
62                + $coercible_with<Never>;
63        }
64        impl $coercible_with<$error> for $error {
65            type Into = $error;
66        }
67        impl $coercible_with<Never> for $error {
68            type Into = $error;
69        }
70        impl $coercible_with<$error> for Never {
71            type Into = $error;
72        }
73        impl $coercible_with<Never> for Never {
74            type Into = Never;
75        }
76
77        /// A shortcut for building the coerced error type,
78        /// given two error types,
79        /// which must both be either `$error` or `Never`.
80        pub type $coerced_error<E1, E2> = <E1 as $coercible_with<E2>>::Into;
81
82        /// A shortcut for building the coerced result type,
83        /// given one value type and two error types,
84        /// which must both be either `$error` or `Never`.
85        pub type $coerced_result<T, E1, E2> = std::result::Result<T, $coerced_error<E1, E2>>;
86    };
87}
88
89/// An "error" type that can never happen.
90///
91/// NB: once the [`never`] types reaches *stable*,
92/// this type will be an alias for the standard type.
93///
94/// [`never`]: https://doc.rust-lang.org/std/primitive.never.html
95///
96#[derive(Clone, Debug)]
97pub enum Never {}
98impl ::std::fmt::Display for Never {
99    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
100        write!(f, "Never")
101    }
102}
103impl std::error::Error for Never {}
104
105/// Type alias for a result that will Never fail.
106pub type OkResult<T> = std::result::Result<T, Never>;
107
108#[cfg(feature = "example_generated")]
109pub mod example_generated;
110
111#[cfg(test)]
112#[macro_use]
113extern crate error_chain;
114
115#[cfg(test)]
116mod test;