Defines another variety of match statement.
Rather than comparing a single value against a number of patterns, the [fn_match] passes each
value to a given function, and evaluates the expression based on which case causes a favorable
return.
The same way a traditional match can act like a sequence of if x == y for various y,
[fn_match] can act like (and expands to) a sequence of if let Some(x) = foo(bar) for various
bar.
Examples
Quick Start
# use fn_match;
# let foo = ; // placeholder
# let bar = ;
# let baz = ;
let x = fn_match! ;
# assert!;
expands into
# use fn_match;
# let foo = ; // placeholder
# let bar = ;
# let baz = ;
let x = if let Some = foo else if let Some = foo else ;
# assert_eq!;
Detailed Example
One use case (and the motivating one) is for when using a Regex
with mutually exclusive capture groups.
# use Regex;
let regex = new.unwrap;
#
# assert!;
# assert!;
# assert!;
# let caps = regex.captures.unwrap;
# assert_eq!;
# assert!;
# assert!;
#
# let caps = regex.captures.unwrap;
# assert_eq!;
# assert!;
# assert!;
#
# let caps = regex.captures.unwrap;
# assert_eq!;
# assert!;
# assert!;
This regex can capture an int literal (235), a String literal ("hello"), or an (unsigned) float
literal (87.43), and could be used to generate instances of the following enum:
In standard Rust, the code to generate a Token from a given &str might look like:
# use Regex;
# let regex = new.unwrap;
#
#
let input = r#""hello world""#;
let caps = regex.captures.expect;
let token = if let Some = caps.name.map else if let Some = caps.name.map else if let Some = caps.name.map else ;
assert_eq!;
Applying [fn_match], as well as an extra closure, allows repetition to be
reduced, and the code to be rewritten as follows:
# use Regex;
# use fn_match;
# let regex = new.unwrap;
#
#
let input = r#"42"#;
let caps = regex.captures.expect;
let f = ;
let token = fn_match! .expect;
assert_eq!;
fn_match Syntax
There are three main components of an [fn_match] block:
- A function to call
- An identifier
- A sequence of lhs-rhs pairs
# use Regex;
# use fn_match;
# let regex = new.unwrap;
#
#
# let input = r#"99.98"#;
# let caps = regex.captures.expect;
#
# let f = ;
# let token = fn_match! .expect;
#
# assert_eq!;
This line provides a function and an identifier. The function (placed in between the fn: and
the =>) needs to take in a single value of the type that the later lines provide, and return
an Option.
Note: the function expression is placed in each if let attempt after expansion. If the
function is a closure, it is recommended to store it in a variable before the fn_match,
to prevent multiple identical closures from being written in the final code.
The identifier (between the => and ;) is used to refer to the contained Some value, if
the function returns that. (It's used as the binding pattern for if let Some(x)).
Following this line are a sequence of match pairs.
# use Regex;
# use fn_match;
# let regex = new.unwrap;
#
#
# let input = r#"99.98"#;
# let caps = regex.captures.expect;
#
# let f = ;
# let token = fn_match! .expect;
#
# assert_eq!;
The left side of the => is the testing value, to be passed into the previously provided
function.
The right side is an expression to be evaluated/returned (wrapped in its own Some) in the
event that the function returned Some when called with the left value. The rhs can also refer
to the identifier as the relevant Some value.
The entire [fn_match] block expression evaluates to an Option. Either
a Some containing the rhs of the lhs that matched, or None if no lhs matched.