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)]
156macro_rules! if_chain {
157 ($($tt:tt)*) => {
158 __if_chain! { @init () $($tt)* }
159 };
160}
161
162#[doc(hidden)]
163#[macro_export(local_inner_macros)]
164macro_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)]
241mod 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