1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//! Idiomatic exceptions.
//!
//! Speed up the happy path of your [`Result`]-based functions by seamlessly using exceptions for
//! error propagation.
//!
//! # Crash course
//!
//! Stick [`#[iex]`](macro@iex) on all the functions that return [`Result`] to make them return an
//! efficiently propagatable `#[iex] Result`, apply `?` just like usual, and occasionally call
//! [`.into_result()`](Outcome::into_result) when you need a real [`Result`]. It's that intuitive.
//!
//! Compared to an algebraic [`Result`], `#[iex] Result` is asymmetric: it sacrifices the
//! performance of error handling, and in return:
//! - Gets rid of branching in the happy path,
//! - Reduces memory usage by never explicitly storing the error or the enum discriminant,
//! - Enables the compiler to use registers instead of memory when wrapping small objects in [`Ok`],
//! - Cleanly separates the happy and unhappy paths in the machine code, resulting in better
//! instruction locality.
//!
//! # Benchmark
//!
//! As a demonstration, we have rewritten [serde](https://serde.rs) and
//! [serde_json](https://crates.io/crates/serde_json) to use `#[iex]` in the deserialization path
//! and used the [Rust JSON Benchmark](https://github.com/serde-rs/json-benchmark) to compare
//! performance. These are the results:
//!
//! <table width="100%">
//! <thead>
//! <tr>
//! <td rowspan="2">Speed (MB/s)</td>
//! <th colspan="2"><code>canada</code></th>
//! <th colspan="2"><code>citm_catalog</code></th>
//! <th colspan="2"><code>twitter</code></th>
//! </tr>
//! <tr>
//! <th>DOM</th>
//! <th>struct</th>
//! <th>DOM</th>
//! <th>struct</th>
//! <th>DOM</th>
//! <th>struct</th>
//! </tr>
//! </thead>
//! <tbody>
//! <tr>
//! <td><a href="https://doc.rust-lang.org/nightly/core/result/enum.Result.html"><code>Result</code></a></td>
//! <td align="center">282.4</td>
//! <td align="center">404.2</td>
//! <td align="center">363.8</td>
//! <td align="center">907.8</td>
//! <td align="center">301.2</td>
//! <td align="center">612.4</td>
//! </tr>
//! <tr>
//! <td><code>#[iex] Result</code></td>
//! <td align="center">282.4</td>
//! <td align="center">565.0</td>
//! <td align="center">439.4</td>
//! <td align="center">1025.4</td>
//! <td align="center">317.6</td>
//! <td align="center">657.8</td>
//! </tr>
//! <tr>
//! <td>Performance increase</td>
//! <td align="center">0%</td>
//! <td align="center">+40%</td>
//! <td align="center">+21%</td>
//! <td align="center">+13%</td>
//! <td align="center">+5%</td>
//! <td align="center">+7%</td>
//! </tr>
//! </tbody>
//! </table>
//!
//! The data is averaged between 5 runs. The repositories for data reproduction are published
//! [on GitHub](https://github.com/orgs/iex-rs/repositories).
//!
//! This benchmark only measures the happy path. When triggered, exceptions are significantly slower
//! than algebraic [`Result`]s. However, it is important to recognize that realistic programs
//! perform actions other than throwing errors, and the slowness of the error path is offset by the
//! increased speed of the happy path. For JSON parsing in particular, the break-even point is 1
//! error per 30-100k bytes parsed, depending on the data.
//!
//! Note that just blindly slapping [`#[iex]`](macro@iex) onto every single function might not
//! increase your performance at best and will decrease it at worst. Like with every other
//! optimization, it is critical to profile code and measure performance on realistic data.
//!
//! # Example
//!
//! ```
//! use iex::{iex, Outcome};
//!
//! #[iex]
//! fn checked_divide(a: u32, b: u32) -> Result<u32, &'static str> {
//! if b == 0 {
//! // Actually raises a custom panic
//! Err("Cannot divide by zero")
//! } else {
//! // Actually returns a / b directly
//! Ok(a / b)
//! }
//! }
//!
//! #[iex]
//! fn checked_divide_by_many_numbers(a: u32, bs: &[u32]) -> Result<Vec<u32>, &'static str> {
//! let mut results = Vec::new();
//! for &b in bs {
//! // Actually lets the panic bubble
//! results.push(checked_divide(a, b)?);
//! }
//! Ok(results)
//! }
//!
//! fn main() {
//! // Actually catches the panic
//! let result = checked_divide_by_many_numbers(5, &[1, 2, 3, 0]).into_result();
//! assert_eq!(result, Err("Cannot divide by zero"));
//! }
//! ```
//!
//! # All you need to know
//!
//! Functions marked [`#[iex]`](macro@iex) are supposed to return a [`Result<T, E>`] in their
//! definition. The macro rewrites them to return an opaque type `#[iex] Result<T, E>` instead. This
//! type implements [`Outcome`], so you can call methods like [`map_err`](Outcome::map_err), but
//! other than that, you must immediately propagate the error via `?`.
//!
//! Alternatively, you can cast it to a [`Result`] via [`.into_result()`](Outcome::into_result).
//! This is the only way to avoid immediate propagation.
//!
//! Doing anything else to the return value, e.g. storing it in a variable and using it later will
//! not cause UB, but will not work the way you think either. If you want to swallow the error, use
//! `let _ = func().into_result();` instead.
//!
//! Directly returning an `#[iex] Result` (obtained from a function call) from another
//! [`#[iex]`](macro@iex) function also works, provided that it's the only `return` statement in the
//! function. Use `Ok(..?)` if there are multiple returns.
//!
//! [`#[iex]`](macro@iex) works on methods. If applied to a function in an `impl Trait for Type`
//! block, the corresponding function in the `trait Trait` block should also be marked with
//! [`#[iex]`](macro@iex). Such traits are not object-safe, unless the method is restricted to
//! `where Self: Sized` (open an issue if you want me to spend time developing a workaround).
pub use ;
use UnsafeCell;
use Exception;
pub use Outcome;
pub use Context;
;
thread_local!
extern crate self as iex;