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;