about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-10-23 22:19:11 +0200
committerGitHub <noreply@github.com>2019-10-23 22:19:11 +0200
commita1514b475854e858a3de95b3806d5795e0d6d72e (patch)
treee7478f199d47d3d1b8252b2acdafb553f8e4d6c3
parent7c043e284a9a3e71ee2e5d34be8e40abf1daa49e (diff)
parente76a1846153209b15dfbf697a33c25214bb753b3 (diff)
downloadrust-a1514b475854e858a3de95b3806d5795e0d6d72e.tar.gz
rust-a1514b475854e858a3de95b3806d5795e0d6d72e.zip
Rollup merge of #65479 - SimonSapin:matches, r=alexcrichton
Add the `matches!( $expr, $pat ) -> bool` macro

# Motivation

This macro is:

* General-purpose (not domain-specific)
* Simple (the implementation is short)
* Very popular [on crates.io](https://crates.io/crates/matches) (currently 37th in all-time downloads)
* The two previous points combined make it number one in [left-pad index](https://twitter.com/bascule/status/1184523027888988160) score

As such, I feel it is a good candidate for inclusion in the standard library.

In fact I already felt that way five years ago: https://github.com/rust-lang/rust/pull/14685 (Although the proof of popularity was not as strong at the time.)

# API

<details>
<del>

Back then, the main concern was that this macro may not be quite universally-enough useful to belong in the prelude.

Therefore, this PR adds the macro such that using it requires one of:

```rust
use core::macros::matches;
use std::macros::matches;
```

</del>
</details>

Like arms of a `match` expression, the macro supports multiple patterns separated by `|` and optionally followed by `if` and a guard expression:

```rust
let foo = 'f';
assert!(matches!(foo, 'A'..='Z' | 'a'..='z'));

let bar = Some(4);
assert!(matches!(bar, Some(x) if x > 2));
```

<details>
<del>

# Implementation constraints

A combination of reasons make it tricky for a standard library macro not to be in the prelude.

Currently, all public `macro_rules` macros in the standard library macros end up “in the prelude” of every crate not through `use std::prelude::v1::*;` like for other kinds of items, but through `#[macro_use]` on `extern crate std;`. (Both are injected by `src/libsyntax_ext/standard_library_imports.rs`.)

`#[macro_use]` seems to import every macro that is available at the top-level of a crate, even if through a `pub use` re-export.

Therefore, for `matches!` not to be in the prelude, we need it to be inside of a module rather than at the root of `core` or `std`.

However, the only way to make a `macro_rules` macro public outside of the crate where it is defined appears to be `#[macro_export]`. This exports the macro at the root of the crate regardless of which module defines it. See [macro scoping](https://doc.rust-lang.org/reference/macros-by-example.html#scoping-exporting-and-importing) in the reference.

Therefore, the macro needs to be defined in a crate that is not `core` or `std`.

# Implementation

This PR adds a new `matches_macro` crate as a private implementation detail of the standard library. This crate is `#![no_core]` so that libcore can depend on it. It contains a `macro_rules` definition with `#[macro_export]`.

libcore and libstd each have a new public `macros` module that contains a `pub use` re-export of the macro. Both the module and the macro are unstable, for now.

The existing private `macros` modules are renamed `prelude_macros`, though their respective source remains in `macros.rs` files.

</del>
</details>
-rw-r--r--src/libcore/macros.rs27
-rw-r--r--src/libstd/lib.rs2
2 files changed, 29 insertions, 0 deletions
diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
index 1320e63df06..35558e3abcd 100644
--- a/src/libcore/macros.rs
+++ b/src/libcore/macros.rs
@@ -238,6 +238,33 @@ macro_rules! debug_assert_ne {
     ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_ne!($($arg)*); })
 }
 
+/// Returns whether the given expression matches any of the given patterns.
+///
+/// Like in a `match` expression, the pattern can be optionally followed by `if`
+/// and a guard expression that has access to names bound by the pattern.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(matches_macro)]
+///
+/// let foo = 'f';
+/// assert!(matches!(foo, 'A'..='Z' | 'a'..='z'));
+///
+/// let bar = Some(4);
+/// assert!(matches!(bar, Some(x) if x > 2));
+/// ```
+#[macro_export]
+#[unstable(feature = "matches_macro", issue = "65721")]
+macro_rules! matches {
+    ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => {
+        match $expression {
+            $( $pattern )|+ $( if $guard )? => true,
+            _ => false
+        }
+    }
+}
+
 /// Unwraps a result or propagates its error.
 ///
 /// The `?` operator was added to replace `try!` and should be used instead.
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 93d3e4ea3df..d0cb0104f6c 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -276,6 +276,7 @@
 #![feature(linkage)]
 #![feature(log_syntax)]
 #![feature(manually_drop_take)]
+#![feature(matches_macro)]
 #![feature(maybe_uninit_ref)]
 #![feature(maybe_uninit_slice)]
 #![feature(needs_panic_runtime)]
@@ -527,6 +528,7 @@ pub use core::{
     writeln,
     // Unstable
     todo,
+    matches,
 };
 
 // Re-export built-in macros defined through libcore.