Expand description
Provides expression macros to match a pattern on a given expression.
Basic Usage
Macros
try_match!
returns Result
: Ok(bindings)
on success or
Err(input)
on failure:
assert_eq!(try_match!(Var1(42), Var1(x)), Ok(42));
assert_eq!(try_match!(Var1(42), Var1(x) if x < 20), Err(Var1(42)));
match_ok!
returns Option
: Some(bindings)
on success or
None
on failure:
assert_eq!(match_ok!(Var1(42), Var1(x)), Some(42));
assert_eq!(match_ok!(Var1(42), Var1(x) if x < 20), None);
unwrap_match!
panics on failure:
assert_eq!(unwrap_match!(Var1(42), Var1(x)), 42);
unwrap_match!(Var1(42), Var1(x) if x < 20);
Implicit Mapping
Requires implicit_map
Cargo feature (enabled by default)
use try_match::unwrap_match;
#[derive(Debug, PartialEq)]
enum Enum<T> { Var1(T), Var2 }
use Enum::{Var1, Var2};
// `()` if there are no bound variables
assert_eq!(unwrap_match!(Var1(42), Var1(_)), ());
// The bound variable if there is exactly one bound variable
assert_eq!(unwrap_match!(Var1(42), Var1(x)), 42);
// An anonymous struct if there are multiple bound variables
let vars = unwrap_match!(Var1((12, 34)), Var1((a, b)));
assert_eq!((vars.a, vars.b), (12, 34));
It produces a tuple if you name the bound variables like _0
, _1
, _2
,
…:
let (a, b) = unwrap_match!(Var1((12, 34)), Var1((_0, _1)));
assert_eq!((a, b), (12, 34));
It’s an error to specify non-contiguous binding indices:
unwrap_match!(Var1((12, 34)), Var1((_0, _2)));
Explicit Mapping
An optional =>
clause specifies an explicit output mapping:
// The right-hand side of `=>` if successful
assert_eq!(unwrap_match!(Var1(42), Var1(x) => x + 1), 43);
assert_eq!(unwrap_match!(Var2::<u32>, Var2 => "yay"), "yay");
Partial Application
Omit the scrutinee expression to produce a closure:
let _: Result<i32, _> = try_match!(Var1(42), Var1(x));
let _: fn(Enum<i32>) -> Result<i32, _> = try_match!( , Var1(x));
assert_eq!(try_match!(Var1(42), Var1(x)), Ok(42));
// ^^^^^^^^ -------
assert_eq!(try_match!(, Var1(x))(Var1(42)), Ok(42));
// ------- ^^^^^^^^
// Equivalent to:
assert_eq!((|x| try_match!(x, Var1(x)))(Var1(42)), Ok(42));
// ------- ^^^^^^^^
let array = [Var1(42), Var2, Var1(10)];
let filtered: Result<Vec<_>, _> = array
.iter()
.map(try_match!(, &Var1(_0) if _0 > 20))
.collect();
// `Var2` is the first value that doesn't match
assert_eq!(filtered, Err(&Var2));
Caveat: Since this mode is implemented by a closure, the default binding mode (RFC 2005), ownership, and control flow may work differently.
Examples (click to show)
try_match!(&Some(42), Some(_0));
try_match!(&Some(42), &Some(ref _0));
try_match!(, Some(_0))(&Some(42));
// ERROR: expected enum `Option`, found reference
use std::rc::Rc;
// `rc2` is conditionally dropped
let rc1 = Rc::new(());
let rc2 = Rc::clone(&rc1);
try_match!(None::<()>, Some(_) => drop(rc2));
assert_eq!(Rc::strong_count(&rc1), 2);
// `rc2` is unconditionally moved into a closure and dropped
let rc1 = Rc::new(());
let rc2 = Rc::clone(&rc1);
try_match!(, Some(_) => drop(rc2))(None::<()>);
assert_eq!(Rc::strong_count(&rc1), 1);
fn func_uncurried() {
try_match!((), () => return);
unreachable!();
}
fn func_curried() -> i32 {
try_match!(, () => return Ok(()))(());
42 // reachable
}
func_uncurried();
func_curried();
Unstable Features
Requires unstable
Cargo feature, exempt from semver guarantees.
No unstable features are defined in this version.
Quirks
Macros Inside Patterns
When using implicit mapping, bind variables defined inside macros are
not recognized because, at the point of try_match
’s macro expansion,
these macros are not expanded yet.
Input Ownership
try_match!
moves a value out of the place represented by the input
expression to return it on failure. Make sure to pass a reference if this is
not desired.
#[derive(Debug)] struct UncopyValue;
let array = [Some(UncopyValue), None];
// ERROR: Can't move out of `array[0]`
let _: &UncopyValue = try_match!(array[0], Some(ref x)).unwrap();
let _: &UncopyValue = try_match!(&array[0], Some(x)).unwrap();
match_ok!
and unwrap_match!
do not have this issue:
#[derive(Debug)] struct UncopyValue;
let array = [Some(UncopyValue), None];
let _: &UncopyValue = match_ok!(array[0], Some(ref x)).unwrap();
#[derive(Debug)] struct UncopyValue;
let array = [Some(UncopyValue), None];
let _: &UncopyValue = unwrap_match!(array[0], Some(ref x));
Binding/Constant Disambiguation
An identifier in a pattern is either a variable binding or a constant pattern, and these cannot be distinguished syntactically. To address this problem, the implicit mapper employs heuristics based on the standard naming conventions (RFC 430).
const ZERO: i32 = 0;
// Binding: `zero` matches regex /^_?[a-z0-9]/
assert_eq!(try_match!(42, zero), Ok(42));
// Constant: `ZERO` matches regex /^_?[A-Z]/
assert_eq!(try_match!(42, ZERO), Err(42));
// Binding: Only a binding can have a subpattern
assert_eq!(try_match!(42, THE_ANSWER @ _), Ok(42));
// ERROR: ambiguous identifier pattern
assert_eq!(try_match!(42, 你好), Ok(42));
Macros
- Try to match
$in
against a given pattern$p
. ProducesSome($out)
if successful;None
otherwise. - Try to match
$in
against a given pattern$p
. ProducesOk($out)
if successful;Err($in)
otherwise. - Try to match
$in
against a given pattern$p
. Panics on failure.