From f69293ae808dea61a2dacee6057ca5bb0d7dc817 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 16 Oct 2019 22:40:37 +0200 Subject: Add `core::macros::matches!( $expr, $pat ) -> bool` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 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.) Back then, the main concern was that this macro may not be quite universally-enough useful to belong in the prelude. # API Therefore, this PR adds the macro such that using it requires one of: ``` use core::macros::matches; use std::macros::matches; ``` Like arms of a `match` expression, the macro supports multiple patterns separated by `|` and optionally followed by `if` and a guard expression: ``` let foo = 'f'; assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); let bar = Some(4); assert!(matches!(bar, Some(x) if x > 2)); ``` # 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. --- src/libstd/lib.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/libstd') diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 93d3e4ea3df..4c079108e2e 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)] @@ -353,7 +354,16 @@ extern crate cfg_if; // The standard macros that are not built-in to the compiler. #[macro_use] -mod macros; +#[path = "macros.rs"] +mod prelude_macros; + +/// Macros that are not in the prelude and need to be imported explicitly +#[unstable(feature = "matches_macro", issue = "0")] +pub mod macros { + #[unstable(feature = "matches_macro", issue = "0")] + #[doc(inline)] + pub use core::macros::matches; +} // The Rust prelude pub mod prelude; -- cgit 1.4.1-3-g733a5