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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/// Use unwinding for error propagation.
///
/// This attribute can be applied to functions and closures.
///
/// Applying this attribute to a function or a closure that returns [`Result<T, E>`] turns it into a
/// function/closure that returns `#[iex] Result<T, E>`. This is an opaque type, but it implements
/// the [`Outcome`](crate::Outcome) trait, so you can use
/// [`.into_result()`](crate::Outcome::into_result) to turn it into [`Result<T, E>`].
///
/// Additionally, `expr?` inside `#[iex]`-wrapped code is interpreted as a custom operator (as
/// opposed to the built-in try operator) that propagates the error from a [`Result<T, E>`] or an
/// `#[iex] Result<T, E>` and returns a `T`.
///
/// **Closure support is incomplete and nightly-only.**
///
/// # Pitfalls
///
/// The lifetimes may be a bit difficult to get right.
///
/// ## Functions and lifetimes
///
/// If a function takes an argument whose *type* has an elided lifetime *parameter*, this parameter
/// must be specified explicitly:
///
/// ```
/// use iex::iex;
/// use std::marker::PhantomData;
///
/// struct A<'a>(PhantomData<&'a ()>);
///
/// #[iex]
/// fn good(a: A<'_>) -> Result<(), ()> { Ok(()) }
///
/// // #[iex]
/// // fn bad(a: A) -> Result<(), ()> { Ok(()) }
/// ```
///
/// This is the conventional way to specify elided lifetimes on structs, so it shouldn't be a
/// nuisance.
///
/// Additionally, if an associated function captures the lifetime from the `impl` block that is not
/// mentioned in its signature, this lifetime must be specified explicitly:
///
/// ```
/// use iex::iex;
/// use std::marker::PhantomData;
///
/// struct Ref<'a, T>(Option<&'a T>);
///
/// impl<'a, T: Clone> Ref<'a, T> {
/// // If there were more lifetimes to list, you'd use #[iex(captures = "'a", captures = "'b")]
/// #[iex(captures = "'a")]
/// fn get(self) -> Result<T, ()> {
/// self.0.cloned().ok_or(())
/// }
/// }
/// ```
///
/// Don't waste time adding the capture clause everywhere, just look out for errors like this one:
///
/// ```text
/// error[E0700]: hidden type for `impl Outcome` captures lifetime that does not appear in bounds
/// --> src/lib.rs:130:5
/// |
/// 10 | impl<'a, T: Clone> Ref<'a, T> {
/// | -- hidden type `IexResult<..>` captures the lifetime `'a` as defined here
/// 11 | #[iex]
/// | ------ opaque type defined here
/// 12 | / fn get(self) -> Result<T, ()> {
/// 13 | | self.0.cloned().ok_or(())
/// 14 | | }
/// | |_____^
/// ```
///
/// Finally, make sure to use the same lifetimes in `trait` and `impl`:
///
/// ```compile_fail
/// use iex::iex;
///
/// trait Trait {
/// #[iex]
/// fn takes_str(s: &'static str) -> Result<(), ()>;
/// }
///
/// impl Trait for () {
/// // error[E0308]: method not compatible with trait
/// // Use 's: &'static str' instead
/// #[iex]
/// fn takes_str(s: &str) -> Result<(), ()> {
/// Ok(())
/// }
/// }
/// ```
///
/// ## Closures
///
/// `#[iex]` closures can't take arguments whose types contain non-`'static` lifetimes. Sorry. Also,
/// you need the nightly features
/// [`stmt_expr_attributes`](https://github.com/rust-lang/rust/issues/15701) and
/// [`proc_macro_hygiene`](https://github.com/rust-lang/rust/issues/54727) to be enabled.
///
/// ## `?` in macros
///
/// `#[iex]` needs to replace the `?` operator with a custom implementation in the function body.
/// This notably fails if the `?` is generated by a macro:
///
/// ```compile_fail
/// use iex::iex;
///
/// #[iex]
/// fn returns_result(x: i32) -> Result<i32, i32> { Ok(x) }
///
/// macro_rules! ok {
/// ($e:expr) => {
/// // the `?` operator cannot be applied to type `..`
/// returns_result($e)?
/// };
/// }
///
/// #[iex]
/// fn test() -> Result<i32, i32> {
/// Err(ok!(123))
/// }
/// ```
///
/// # Attributes
///
/// Rust evaluates attribute macros from top to bottom, so if `#[iex]` is not the only attribute
/// macro applied to the function/closure, the macros listed above it will be applied to the
/// original definition, and the macros listed below it will be applied to an internal closure
/// generated by `#[iex]`.
///
/// Note that this only applies to attribute *macros*; normal attributes, such as `#[inline]` and
/// `#[cfg]`, do the right thing independently from their location.
///
/// # Documentation
///
/// `#[iex]` functions are documented (by rustdoc) to return an algebraic [`Result`], just like in
/// source code, but they also have an `#[iex]` macro attached to their signature. This is a
/// sufficient indicator for those who know what `#[iex]` is, but if you use `#[iex]` in the public
/// API of a library, you probably want to write that down in prose.
///
/// For a rendered example, see [`example`](crate::example).
///
/// # `#[iex(shares = ..)]`
///
/// This use is specific for `map_err` and `inspect_err`. See the documentation for
/// [`Outcome`](crate::Outcome::map_err) for more information.
///
/// # Example
///
/// ```
/// #![feature(stmt_expr_attributes, proc_macro_hygiene)]
/// // The Outcome trait is required for .into_result()
/// use iex::{iex, Outcome};
///
/// fn returning_regular_result<E>(err: E) -> Result<(), E> { Err(err) }
///
/// #[iex]
/// fn returning_iex_result<E>(err: E) -> Result<(), E> { Err(err) }
///
/// #[iex]
/// fn test() -> Result<i32, String> {
/// // ? can be applied to a Result<_, String>
/// returning_regular_result("Some error happened!".to_string())?;
///
/// // ? can be applied to a Result<_, impl Into<String>> too
/// returning_regular_result("Some error happened!")?;
///
/// // The same applies to #[iex] Result
/// returning_iex_result("Some error happened!".to_string())?;
/// returning_iex_result("Some error happened!")?;
///
/// // Closures work too
/// let closure = #[iex] || Ok(1);
/// closure()?;
///
/// // You can also directly return a Result
/// Ok(123)
/// }
///
/// fn main() {
/// // Using an #[iex] function from a regular function requires a cast
/// let _result: Result<i32, String> = test().into_result();
/// }
/// ```
///
/// This attribute can only be applied to functions that return a [`Result`]:
///
/// ```compile_fail
/// # use iex::iex;
/// // the trait `Outcome` is not implemented for `Option<()>`
/// #[iex]
/// fn invalid_example() -> Option<()> {
/// None
/// }
/// ```
///
/// ```compile_fail
/// # use iex::iex;
/// // the trait `Outcome` is not implemented for `()`
/// #[iex]
/// fn invalid_example() {}
/// ```
pub use iex;
/// Try block.
///
/// This is an implementation of the [nightly `try` blocks][1] for [`#[iex]`](macro@crate::iex).
///
/// # Example
///
/// ```
/// use iex::{iex, Outcome, try_block};
///
/// #[iex]
/// fn fallible() -> Result<i32, ()> { Ok(1) }
///
/// #[iex]
/// fn example() -> Result<(), ()> {
/// // Many operations...
/// let value = try_block! {
/// fallible()?;
/// fallible()?;
/// 2
/// }.inspect_err(|e| println!("{e:?}"))?; // Common error handler
/// assert_eq!(value, 2);
/// // Many other operations...
/// Ok(())
/// }
/// ```
///
/// [1]: https://doc.rust-lang.org/nightly/unstable-book/language-features/try-blocks.html
pub use try_block;