-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the matches!( $expr, $pat ) -> bool
macro
#65479
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
@rust-lang/libs, what do you think? If having this macro in the prelude sounds acceptable after all, this PR can be much simpler. |
I think this concern holds true even more so today. In particular, #53667 (which is being worked on) would allow us to write e.g. |
@@ -0,0 +1,8 @@ | |||
error: cannot find macro `matches` in this scope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move all the related tests into their own directory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What directory? I looked for a prelude
directory and didn’t find one. Or do you mean one just for this macro?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah just one for the macro to keep things organised.
Is there an easy way to get data on (And, anecdotally, the times I see it mentioned in chat are most often for "I'm trying to test something, but I can't use |
I can’t think of one. A not-so-easy way might be to grep the source code of every crate in https://crates.io/crates/matches/reverse_dependencies My anecdotal evidence is that I’ve used |
I feel this isn’t quite the same concern (that was about the bar being very high for adding anything to the prelude), but the rest of your comment makes a fair point. A similar point was made in #14685 with RFC 160’s I feel the same about RFC 2497’s
Can you say more about what this
Is there any concrete proposal for those yet?
While this churn is a valid concern, I think “replicates” is not accurate. This macro supports many cases that
|
Even though I've personally never used
This sounds like an ideal candidate for std to me. However, I would go further and say that we should revisit the decision made about the adding it to the prelude in 2014. nrc's comment at the time doesn't say anything about this macro being particularly unworthy of being in the prelude, just about the fact that adding macros to std means adding them to the prelude. But we've added macros to std and the prelude since then, and this would be the first macro we add that doesn't get added to the prelude. This would be the first macro we've added to std that isn't in the prelude. I think we should just add this like every other macro and accept that being in the prelude is how macros work, rather than creating this extra crate hack to get around that. Alternatively:
|
Yes, but if
It would be defined as
Not yet; we're taking an incremental approach with let_chains.
The question here is not whether
Well there are options to chose from here which include e.g. making the scope short or long (and the latter is forward compatible with the latter).
The syntax has previously been implemented experimentally in rust-lang/rfcs#2260 (comment) and
Implementing the syntax of #53667 in a sane way already does so for
In this case, I think this exposes the fact that there's a missing language feature. |
My understanding of RFC 2497 is that macro_rules! is {
(let $p: pat = $e:expr $( && $guard: expr )*) => {…}
} Or something recursive, to support additional |
That's what the RFC specifies but it isn't really implementable in a good way so the actual change on nightly was instead to just add |
It's possible to achieve the same observable behavior (public, but not in prelude) without introducing new crates and modules, by using a |
I don't know whether it's reasonable to add a library solution for this for the time until a language solution is ready, but I find the |
Like the macro’s name, the |
I don't really have much of an opinion on whether or not to add this macro, I'm fine either way. On the topic of organization, though, and responding to @withoutboats's comment above I agree that adding this shouldn't require build system hacks and weird crates. I would personally say that if we're going to add this we should do one of two things:
For consistency with all other exported macros I would probably go with adding it to the prelude. If we're adding a macro to libcore/libstd and we don't think we should add it to the prelude, then we probably shouldn't be adding the macro to libcore/libstd. |
Adding to the prelude would be my preference, and indeed would make this PR much simpler. I submitted it this way based on earlier conversations with libs team members saying that a non-prelude macro would be more likely to be accepted.
This is what I tried to do, but see PR description. Not having the At the moment, being importable from the root of |
☔ The latest upstream changes (presumably #65671) made this pull request unmergeable. Please resolve the merge conflicts. |
I’ll rebase after we decide on whether we want this in the prelude. |
@rfcbot ask libs "Are we alright adding To make progress, could people check the box if they don't object to adding a |
Team member @withoutboats has asked teams: T-libs, for consensus on:
|
# 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: rust-lang#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.
core::macros::matches!( $expr, $pat ) -> bool
matches!( $expr, $pat ) -> bool
macro
@bors: r+ |
📌 Commit e76a184 has been approved by |
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: rust-lang#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>
Rollup of 12 pull requests Successful merges: - #64178 (More Clippy fixes for alloc, core and std) - #65144 (Add Cow::is_borrowed and Cow::is_owned) - #65193 (Lockless LintStore) - #65479 (Add the `matches!( $expr, $pat ) -> bool` macro) - #65518 (Avoid ICE when checking `Destination` of `break` inside a closure) - #65583 (rustc_metadata: use a table for super_predicates, fn_sig, impl_trait_ref.) - #65641 (Derive `Rustc{En,De}codable` for `TokenStream`.) - #65648 (Eliminate `intersect_opt`.) - #65657 (Remove `InternedString`) - #65691 (Update E0659 error code long explanation to 2018 edition) - #65696 (Fix an issue with const inference variables sticking around under Chalk + NLL) - #65704 (relax ExactSizeIterator bound on write_bytes) Failed merges: r? @ghost
Add the `cfg_match!` macro # Movitation Adds a match-like version of the `cfg_if` crate without a RFC [for the same reasons that caused `matches!` to be included in the standard library](rust-lang#65479). * General-purpose (not domain-specific) * Simple (the implementation is short) and useful (things can become difficult with several `cfg`s) * Very popular [on crates.io ](https://crates.io/crates/cfg-if) (currently 3th in all-time downloads) * The two previous points combined make it number three in [left-pad index](https://twitter.com/bascule/status/1184523027888988160) score ```rust match_cfg! { cfg(unix) => { fn foo() { /* unix specific functionality */ } } cfg(target_pointer_width = "32") => { fn foo() { /* non-unix, 32-bit functionality */ } } _ => { fn foo() { /* fallback implementation */ } } } ``` # Considerations A match-like syntax feels more natural in the sense that each macro fragment resembles an arm but I personally don't mind switching to any other desired syntax. The lack of `#[ ... ]` is intended to reduce typing, nevertheless, the same reasoning described above can also be applied to this aspect. Since blocks are intended to only contain items, anything but `cfg` is not expected to be supported at the current or future time. ~~Credits goes to `@gnzlbg` because most of the code was shamelessly copied from https://github.com/gnzlbg/match_cfg.~~ Credits goes to `@alexcrichton` because most of the code was shamelessly copied from https://github.com/rust-lang/cfg-if.
Add the `cfg_match!` macro # Movitation Adds a match-like version of the `cfg_if` crate without a RFC [for the same reasons that caused `matches!` to be included in the standard library](rust-lang#65479). * General-purpose (not domain-specific) * Simple (the implementation is short) and useful (things can become difficult with several `cfg`s) * Very popular [on crates.io ](https://crates.io/crates/cfg-if) (currently 3th in all-time downloads) * The two previous points combined make it number three in [left-pad index](https://twitter.com/bascule/status/1184523027888988160) score ```rust match_cfg! { cfg(unix) => { fn foo() { /* unix specific functionality */ } } cfg(target_pointer_width = "32") => { fn foo() { /* non-unix, 32-bit functionality */ } } _ => { fn foo() { /* fallback implementation */ } } } ``` # Considerations A match-like syntax feels more natural in the sense that each macro fragment resembles an arm but I personally don't mind switching to any other desired syntax. The lack of `#[ ... ]` is intended to reduce typing, nevertheless, the same reasoning described above can also be applied to this aspect. Since blocks are intended to only contain items, anything but `cfg` is not expected to be supported at the current or future time. ~~Credits goes to `@gnzlbg` because most of the code was shamelessly copied from https://github.com/gnzlbg/match_cfg.~~ Credits goes to `@alexcrichton` because most of the code was shamelessly copied from https://github.com/rust-lang/cfg-if.
Motivation
This macro is:
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: #14685 (Although the proof of popularity was not as strong at the time.)
API
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:
Like arms of a
match
expression, the macro supports multiple patterns separated by|
and optionally followed byif
and a guard expression: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 throughuse std::prelude::v1::*;
like for other kinds of items, but through#[macro_use]
onextern crate std;
. (Both are injected bysrc/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 apub 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 ofcore
orstd
.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 in the reference.Therefore, the macro needs to be defined in a crate that is not
core
orstd
.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 amacro_rules
definition with#[macro_export]
.libcore and libstd each have a new public
macros
module that contains apub use
re-export of the macro. Both the module and the macro are unstable, for now.The existing private
macros
modules are renamedprelude_macros
, though their respective source remains inmacros.rs
files.