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] simple()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] empty()257 fn empty() { 258 let success; 259 if_chain! { 260 then { success = true; } 261 } 262 assert!(success); 263 } 264 265 #[test] empty_with_else()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] if_let_multiple_patterns()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] let_multiple_patterns()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] let_type_annotation_patterns()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