if_chain/
lib.rs

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