1 | //! This crate provides a single macro called `if_chain!`. |
2 | //! |
3 | //! `if_chain!` lets you write long chains of nested `if` and `if let` |
4 | //! statements without the associated rightward drift. It also supports multiple |
5 | //! patterns (e.g. `if let Foo(a) | Bar(a) = b`) in places where Rust would |
6 | //! normally not allow them. |
7 | //! |
8 | //! See the associated [blog post] for the background behind this crate. |
9 | //! |
10 | //! [blog post]: https://lambda.xyz/blog/if-chain |
11 | //! |
12 | //! # Note about recursion limits |
13 | //! |
14 | //! If you run into "recursion limit reached" errors while using this macro, try |
15 | //! adding |
16 | //! |
17 | //! ```rust,ignore |
18 | //! #![recursion_limit = "1000" ] |
19 | //! ``` |
20 | //! |
21 | //! to the top of your crate. |
22 | //! |
23 | //! # Examples |
24 | //! |
25 | //! ## Quick start |
26 | //! |
27 | //! ```rust,ignore |
28 | //! if_chain! { |
29 | //! if let Some(y) = x; |
30 | //! if y.len() == 2; |
31 | //! if let Some(z) = y; |
32 | //! then { |
33 | //! do_stuff_with(z); |
34 | //! } |
35 | //! } |
36 | //! ``` |
37 | //! |
38 | //! becomes |
39 | //! |
40 | //! ```rust,ignore |
41 | //! if let Some(y) = x { |
42 | //! if y.len() == 2 { |
43 | //! if let Some(z) = y { |
44 | //! do_stuff_with(z); |
45 | //! } |
46 | //! } |
47 | //! } |
48 | //! ``` |
49 | //! |
50 | //! ## Fallback values with `else` |
51 | //! |
52 | //! ```rust,ignore |
53 | //! if_chain! { |
54 | //! if let Some(y) = x; |
55 | //! if let Some(z) = y; |
56 | //! then { |
57 | //! do_stuff_with(z) |
58 | //! } else { |
59 | //! do_something_else() |
60 | //! } |
61 | //! } |
62 | //! ``` |
63 | //! |
64 | //! becomes |
65 | //! |
66 | //! ```rust,ignore |
67 | //! if let Some(y) = x { |
68 | //! if let Some(z) = y { |
69 | //! do_stuff_with(z) |
70 | //! } else { |
71 | //! do_something_else() |
72 | //! } |
73 | //! } else { |
74 | //! do_something_else() |
75 | //! } |
76 | //! ``` |
77 | //! |
78 | //! ## Intermediate variables with `let` |
79 | //! |
80 | //! ```rust,ignore |
81 | //! if_chain! { |
82 | //! if let Some(y) = x; |
83 | //! let z = y.some().complicated().expression(); |
84 | //! if z == 42; |
85 | //! then { |
86 | //! do_stuff_with(y); |
87 | //! } |
88 | //! } |
89 | //! ``` |
90 | //! |
91 | //! becomes |
92 | //! |
93 | //! ```rust,ignore |
94 | //! if let Some(y) = x { |
95 | //! let z = y.some().complicated().expression(); |
96 | //! if z == 42 { |
97 | //! do_stuff_with(y); |
98 | //! } |
99 | //! } |
100 | //! ``` |
101 | //! |
102 | //! ## Type ascription |
103 | //! |
104 | //! ```rust,ignore |
105 | //! let mut x = some_generic_computation(); |
106 | //! if_chain! { |
107 | //! if x > 7; |
108 | //! let y: u32 = another_generic_computation(); |
109 | //! then { x += y } |
110 | //! else { x += 1 } |
111 | //! } |
112 | //! ``` |
113 | //! |
114 | //! becomes |
115 | //! |
116 | //! ```rust,ignore |
117 | //! let mut x = some_generic_computation(); |
118 | //! if x > 7 { |
119 | //! let y: u32 = another_generic_computation(); |
120 | //! x += y |
121 | //! } else { |
122 | //! x += 1 |
123 | //! } |
124 | //! ``` |
125 | //! |
126 | //! ## Multiple patterns |
127 | //! |
128 | //! ```rust,ignore |
129 | //! if_chain! { |
130 | //! if let Foo(y) | Bar(y) | Baz(y) = x; |
131 | //! let Bubbles(z) | Buttercup(z) | Blossom(z) = y; |
132 | //! then { do_stuff_with(z) } |
133 | //! } |
134 | //! ``` |
135 | //! |
136 | //! becomes |
137 | //! |
138 | //! ```rust,ignore |
139 | //! match x { |
140 | //! Foo(y) | Bar(y) | Baz(y) => match y { |
141 | //! Bubbles(z) | Buttercup(z) | Blossom(z) => do_stuff_with(z) |
142 | //! }, |
143 | //! _ => {} |
144 | //! } |
145 | //! ``` |
146 | //! |
147 | //! Note that if you use a plain `let`, then `if_chain!` assumes that the |
148 | //! pattern is *irrefutable* (always matches) and doesn't add a fallback branch. |
149 | |
150 | #![cfg_attr (not(test), no_std)] |
151 | |
152 | /// Macro for writing nested `if let` expressions. |
153 | /// |
154 | /// See the crate documentation for information on how to use this macro. |
155 | #[macro_export (local_inner_macros)] |
156 | macro_rules! if_chain { |
157 | ($($tt:tt)*) => { |
158 | __if_chain! { @init () $($tt)* } |
159 | }; |
160 | } |
161 | |
162 | #[doc (hidden)] |
163 | #[macro_export (local_inner_macros)] |
164 | macro_rules! __if_chain { |
165 | // Expand with both a successful case and a fallback |
166 | (@init ($($tt:tt)*) then { $($then:tt)* } else { $($other:tt)* }) => { |
167 | __if_chain! { @expand { $($other)* } $($tt)* then { $($then)* } } |
168 | }; |
169 | // Expand with no fallback |
170 | (@init ($($tt:tt)*) then { $($then:tt)* }) => { |
171 | __if_chain! { @expand {} $($tt)* then { $($then)* } } |
172 | }; |
173 | // Munch everything until either of the arms above can be matched. |
174 | // Munched tokens are placed into `$($tt)*` |
175 | (@init ($($tt:tt)*) $head:tt $($tail:tt)*) => { |
176 | __if_chain! { @init ($($tt)* $head) $($tail)* } |
177 | }; |
178 | |
179 | // `let` with a single pattern |
180 | (@expand { $($other:tt)* } let $pat:pat = $expr:expr; $($tt:tt)+) => { |
181 | { |
182 | let $pat = $expr; |
183 | __if_chain! { @expand { $($other)* } $($tt)+ } |
184 | } |
185 | }; |
186 | // `let` with a single identifier and a type hint |
187 | (@expand { $($other:tt)* } let $ident:ident: $ty:ty = $expr:expr; $($tt:tt)+) => { |
188 | { |
189 | let $ident: $ty = $expr; |
190 | __if_chain! { @expand { $($other)* } $($tt)+ } |
191 | } |
192 | }; |
193 | // `let` with multiple patterns |
194 | (@expand { $($other:tt)* } let $pat1:pat | $($pat:pat)|+ = $expr:expr; $($tt:tt)+) => { |
195 | match $expr { |
196 | $pat1 | $($pat)|+ => { __if_chain! { @expand { $($other)* } $($tt)+ } } |
197 | } |
198 | }; |
199 | // `if let` with a single pattern |
200 | (@expand {} if let $pat:pat = $expr:expr; $($tt:tt)+) => { |
201 | if let $pat = $expr { |
202 | __if_chain! { @expand {} $($tt)+ } |
203 | } |
204 | }; |
205 | // `if let` with a single pattern and a fallback |
206 | (@expand { $($other:tt)+ } if let $pat:pat = $expr:expr; $($tt:tt)+) => { |
207 | if let $pat = $expr { |
208 | __if_chain! { @expand { $($other)+ } $($tt)+ } |
209 | } else { |
210 | $($other)+ |
211 | } |
212 | }; |
213 | // `if let` with multiple matterns and a fallback (if present) |
214 | (@expand { $($other:tt)* } if let $pat1:pat | $($pat:pat)|+ = $expr:expr; $($tt:tt)+) => { |
215 | match $expr { |
216 | $pat1 | $($pat)|+ => { __if_chain! { @expand { $($other)* } $($tt)+ } }, |
217 | _ => { $($other)* } |
218 | } |
219 | }; |
220 | // `if` with a successful case |
221 | (@expand {} if $expr:expr; $($tt:tt)+) => { |
222 | if $expr { |
223 | __if_chain! { @expand {} $($tt)+ } |
224 | } |
225 | }; |
226 | // `if` with both a successful case and a fallback |
227 | (@expand { $($other:tt)+ } if $expr:expr; $($tt:tt)+) => { |
228 | if $expr { |
229 | __if_chain! { @expand { $($other)+ } $($tt)+ } |
230 | } else { |
231 | $($other)+ |
232 | } |
233 | }; |
234 | // Final macro call |
235 | (@expand { $($other:tt)* } then { $($then:tt)* }) => { |
236 | $($then)* |
237 | }; |
238 | } |
239 | |
240 | #[cfg (test)] |
241 | mod tests { |
242 | #[test ] |
243 | fn simple() { |
244 | let x: Option<Result<Option<String>, (u32, u32)>> = Some(Err((41, 42))); |
245 | let mut success = false; |
246 | if_chain! { |
247 | if let Some(y) = x; |
248 | if let Err(z) = y; |
249 | let (_, b) = z; |
250 | if b == 42; |
251 | then { success = true; } |
252 | } |
253 | assert!(success); |
254 | } |
255 | |
256 | #[test ] |
257 | fn empty() { |
258 | let success; |
259 | if_chain! { |
260 | then { success = true; } |
261 | } |
262 | assert!(success); |
263 | } |
264 | |
265 | #[test ] |
266 | fn empty_with_else() { |
267 | let success; |
268 | if_chain! { |
269 | then { success = true; } |
270 | else { unreachable!(); } |
271 | } |
272 | assert!(success); |
273 | } |
274 | |
275 | #[test ] |
276 | fn if_let_multiple_patterns() { |
277 | #[derive (Copy, Clone)] |
278 | enum Robot { Nano, Biscuit1, Biscuit2 } |
279 | for &(robot, expected) in &[ |
280 | (Robot::Nano, false), |
281 | (Robot::Biscuit1, true), |
282 | (Robot::Biscuit2, true), |
283 | ] { |
284 | let is_biscuit = if_chain! { |
285 | if let Robot::Biscuit1 | Robot::Biscuit2 = robot; |
286 | then { true } else { false } |
287 | }; |
288 | assert_eq!(is_biscuit, expected); |
289 | } |
290 | } |
291 | |
292 | #[test ] |
293 | fn let_multiple_patterns() { |
294 | let x: Result<u32, u32> = Ok(42); |
295 | if_chain! { |
296 | let Ok(x) | Err(x) = x; |
297 | then { assert_eq!(x, 42); } |
298 | else { panic!(); } |
299 | } |
300 | } |
301 | |
302 | #[test ] |
303 | fn let_type_annotation_patterns() { |
304 | let mut x = 1; |
305 | if_chain! { |
306 | if x > 0; |
307 | let y: u32 = 2; |
308 | |
309 | then { x += y; } |
310 | else { x += 1; } |
311 | } |
312 | assert_eq!(x, 3); |
313 | } |
314 | } |
315 | |