matching!() { /* proc-macro */ }
Expand description

Macro to ease argument pattern matching. This macro produces a closure expression suitable for passing to build::Each::call.

Takes inspiration from std::matches and works similarly, except that the value to match can be removed as a macro argument, since it is instead received as the closure argument.

Unimock uses tuples to represent multiple arguments. A single argument is not a tuple. To avoid the extra set of parentheses for simple multi-argument matchers, there is a special syntax that accepts several top-level patterns: matching!("a", "b") will expand to matching!(("a", "b")).

Example


fn one_str() {
    fn args(_: impl Fn(&(&str)) -> bool) {}
    args(matching!("a"));
}

fn three_strs() {
    fn args(_: impl Fn(&(&str, &str, &str)) -> bool) {}
    args(matching!("a", _, "c"));
    args(matching!(("a", "b", "c") | ("d", "e", "f")));
    args(matching!(("a", b, "c") if b.contains("foo")));
}

Auto-“coercions”

Since the input expression being matched is generated by the macro, you would normally suffer from the following problem when matching some non-&str function input:

let string = String::new();
match &string {
    "foo" => true, // expected struct `String`, found `str`
    _ => false,
}

To help ergonomics, the matching macro recognizes certain literals used in the patterns, and performs appropriate type conversion at the correct places:

struct Newtype(String);

fn exotic_strings() {
    fn args(_: impl Fn(&(String, std::borrow::Cow<'static, str>, Newtype, i32)) -> bool) {}
    args(matching!(("a", _, "c", _) | (_, "b", _, 42)));
}

// Newtype works by implementing the following:
impl std::convert::AsRef<str> for Newtype {
    fn as_ref(&self) -> &str {
        self.0.as_str()
    }
}

Internally it works by calling macro_api::as_str_ref on inputs matched by a string literal.