Crate try_match

source ·
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. Produces Some($out) if successful; None otherwise.
  • Try to match $in against a given pattern $p. Produces Ok($out) if successful; Err($in) otherwise.
  • Try to match $in against a given pattern $p. Panics on failure.