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
//! Provides an expression macro `try_match` that performs pattern //! matching and returns the bound variables via `Ok(_)` iff successful. //! //! # Examples //! //! ## Explicit Mapping //! //! ``` //! use try_match::try_match; //! //! #[derive(Copy, Clone, Debug, PartialEq)] //! enum Enum<T> { Var1(T), Var2 } //! use Enum::{Var1, Var2}; //! //! // The right-hand side of `=>` if successful //! assert_eq!(try_match!(Var1(x) = Var1(42) => x), Ok(42)); //! assert_eq!(try_match!(Var2 = Var2::<u32> => "yay"), Ok("yay")); //! //! // `Err(input)` on failure //! assert_eq!(try_match!(Var1(x) = Var2::<u32> => x), Err(Var2)); //! assert_eq!(try_match!(Var2 = Var1(42) => "yay"), Err(Var1(42))); //! ``` //! //! ## Implicit Mapping //! //! `=>` and the part that comes after can be omitted (requires `implicit_map` //! feature, which is enabled by default; you can disable it to skip the //! compilation of the internal procedural macro): //! //! ``` //! # use try_match::try_match; //! # #[derive(Debug, PartialEq)] enum Enum<T> { Var1(T), Var2 } //! # use Enum::{Var1, Var2}; //! // `()` if there are no bound variables //! assert_eq!(try_match!(Var1(_) = Var1(42)), Ok(())); //! //! // The bound variable if there is exactly one bound variables //! assert_eq!(try_match!(Var1(x) = Var1(42)), Ok(42)); //! //! // An anonymous struct if there are multiple bound variables //! let vars = try_match!(Var1((a, b)) = Var1((12, 34))).unwrap(); //! assert_eq!((vars.a, vars.b), (12, 34)); //! ``` //! //! It produces a tuple if you name the bound variables like `_0`, `_1`, `_2`, //! ...: //! //! ``` //! # use try_match::try_match; //! # #[derive(Debug, PartialEq)] enum Enum<T> { Var1(T), Var2 } //! # use Enum::{Var1, Var2}; //! let (a, b) = try_match!(Var1((_0, _1)) = Var1((12, 34))).unwrap(); //! assert_eq!((a, b), (12, 34)); //! ``` //! //! It's an error to specify non-contiguous binding indices: //! //! ```compile_fail //! # use try_match::try_match; //! # #[derive(Debug, PartialEq)] enum Enum<T> { Var1(T), Var2 } //! # use Enum::{Var1, Var2}; //! let _ = try_match!(Var1((_0, _2)) = Var1((12, 34))); //! ``` //! //! ```compile_fail //! # use try_match::try_match; //! # #[derive(Debug, PartialEq)] enum Enum<T> { Var1(T), Var2 } //! # use Enum::{Var1, Var2}; //! let _ = try_match!(Var1((_0, _9223372036854775808)) = Var1((12, 34))); //! ``` //! //! # Restrictions //! //! - Macros cannot be used in a supplied pattern. //! //! # Related Work: `matches` //! //! [`matches!`] is similar but only returns `bool` indicating whether matching //! was successful or not. //! //! ```no_compile //! let success1 = matches!(Var1(42), Var1(_)); //! let success2 = try_match!(Var1(_) = Var1(42)).is_ok(); //! ``` //! //! [`matches!`]: https://crates.io/crates/matches //! #![no_std] /// Try to match `$in` against a given pattern `$p`. Produces `Ok($out)` if /// successful; `Err($in)` otherwise. /// /// `=> $out` can be left out, in which case it's implied based on the number of /// bound variables in `$p`: /// /// - If there are no bound variables, it is implied to be `()`. /// - If there is exactly one bound variables `var`, it is implied to be `var`. /// - If there are multiple bound variables `var1, var2, ...`, it is implied to /// be `AnonymousType { var1, var2 }`. /// /// `AnonymousType` derives `Clone` and `Copy`, and `Debug` if `std` feature is /// enabled. /// /// See [the crate-level documentation](index.html) for examples. #[macro_export] macro_rules! try_match { ($p:pat = $in:expr => $out:expr) => { match $in { $p => ::core::result::Result::Ok($out), in_value => ::core::result::Result::Err(in_value), } }; // Using `$($in:tt)*` in place of `$in:expr` is a work-around for // <https://github.com/dtolnay/proc-macro-hack/issues/46>, which is // originally caused by <https://github.com/rust-lang/rust/issues/43081> ($p:pat = $($in:tt)*) => { // `$p` needs to be parenthesized for it to work on nightly-2020-05-30 // and syn 1.0.29 $crate::implicit_try_match!(($p) = $($in)*) }; } #[cfg(all(feature = "std", feature = "implicit_map"))] #[macro_export] #[doc(hidden)] macro_rules! implicit_try_match { ($p:pat = $($in:tt)*) => { // `$p` needs to be parenthesized for it to work on nightly-2020-05-30 // and syn 1.0.29 $crate::implicit_try_match_inner!(std($p) = $($in)*) }; } #[cfg(all(not(feature = "std"), feature = "implicit_map"))] #[macro_export] #[doc(hidden)] macro_rules! implicit_try_match { ($p:pat = $($in:tt)*) => { $crate::implicit_try_match_inner!(no_std($p) = $($in)*) }; } #[cfg(not(feature = "implicit_map"))] macro_rules! implicit_try_match { ($p:pat) => { compile_error!( "can't use the implicit mapping form of `try_match!` because \ the feature `implicit_map` is disabled" ) }; } /// Pattern: `(std|no_std) $p:pat` /// /// The produced expression evaluates to `Ok(_)` using bound variables on a /// successful match on the given value. /// /// - If there are no bound variables, it generates `()`. /// - If there is exactly one bound variables `var`, it generates `var`. /// - If there are multiple bound variables `var1, var2, ...`, it generates /// `SomeType { var1, var2 }`. /// /// Otherwise, the expression evaluates to `Err($in)`. /// /// `#[proc_macro_hack]` makes it possible to use this procedural macro in /// expression position without relying on an unstable rustc feature, but with /// some restrictions. See `proc_macro_hack`'s documentation for more. #[cfg(feature = "implicit_map")] #[proc_macro_hack::proc_macro_hack] #[doc(hidden)] pub use try_match_inner::implicit_try_match_inner;