rfcs
rfcs copied to clipboard
RFC: `Foo { .. }` pattern matches non-struct types
Special case struct patterns containing only a rest pattern (e.g., Foo { .. }) so that they can match values of any type with the appropriate name, not just structs (e.g., it could match an enum Foo value). This is done so that structs containing only private fields can be changed to other types without breaking backwards compatibility.
a related pet peeve: you can use Ty { 0: abc, 1: def } syntax on tuple structs or type aliases of tuple structs but not on type aliases of actual tuples, which makes macros more annoying
I'm against this change.
A pattern is generally an inverse of an expression with the same syntax — & expression creates a reference, & pattern dereference it; S { a } expression creates a struct S from pieces, S { a } pattern destructures it; ... (an exception is ref vs *). This proposal muddies this concept, since it allows matching an option with Option { .. }, while an option can't be constructed with Option { ... } (... being a placeholder for literally anything).
I'm inclined to say that having the intuition of "if I can match it with a pattern, then I can create the value with a similar syntax" is good, and so breaking it is not so good.
I might have had a different opinion be this a proposal for a language with a good server story. But since rust's semver story is quite bad, I don't think this small improvement can justify the special case.
This is not to say we shouldn't improve rust's semver story. But in this particular case the downsides outweigh the improvement in an edge case.
(somewhat off-topic, apologies!)
But since rust's semver story is quite bad [...] This is not to say we shouldn't improve rust's semver story.
@WaffleLapkin I'd love to chat with you sometime about this so I can learn more! I'm always on the lookout for ways I can positively contribute in this area, including by making cargo-semver-checks better.
There is a benefit with this change other than semver compatibility - it can be useful to have an explicit type in a pattern instead of _, either for readability or to guard yourself against refactors where that type might change. I actually ran into a case recently where I wanted to do this:
match something {
SomeStruct(SomeEnum { .. }, ....) => .., // future-proof: it's important that SomeEnum exists here
...
...instead I decided to do:
match something {
SomeStruct(val, ....) => {
let _: SomeEnum = val; // future-proof: it's important that SomeEnum exists here
...
@camsteffen IMO it would be better to have explicit syntax for annotating types in patterns, if that's the goal.
I did a cursory search manually for code on github that uses a pattern like Foo { .. }. It appears that there is some amount of people that uses such a pattern, probably to make sure that the value has the expected type. Here are some examples.
Code that uses this pattern on a struct with all-private fields from an external crate:
- https://github.com/buckyos/CYFS/blob/main/src/component/cyfs-lib/src/ws/session.rs#L245
- https://github.com/postgresml/pgcat/blob/main/src/dns_cache.rs#L374
- https://github.com/worldcoin/orb-software/blob/main/mcu-util/src/orb/security_board.rs#L402
- https://github.com/0xPolygonZero/zk_evm/blob/develop/zero/src/env.rs#L13
- https://github.com/calyxir/calyx/blob/main/calyx-backend/src/mlir.rs#L40
- https://github.com/Shizcow/dmenu-rs/blob/master/src/stest/src/main.rs#L19
Code that uses this pattern on a struct with public fields from an external crate:
- https://github.com/rust-lang/hashbrown/blob/master/src/map.rs#L5948
- https://github.com/ZettaScaleLabs/stabby/blob/main/stabby-macros/src/traits.rs#L1271
- https://github.com/Gankra/abi-cafe/blob/main/src/toolchains/c/declare.rs#L271-L276
- https://github.com/FuelLabs/sway/blob/master/forc-plugins/forc-doc/src/doc/mod.rs#L225
Code that uses this pattern on a struct defined in the same crate:
- https://github.com/rust-lang/rust/blob/master/library/alloc/tests/c_str2.rs#L112-L116
- https://github.com/google/zerocopy/blob/main/src/wrappers.rs#L494-L495
- https://github.com/slackhq/hakana/blob/main/src/code_info/ttype/comparison/atomic_type_comparator.rs#L218
- https://github.com/oli-obk/ui_test/blob/main/src/status_emitter/text.rs#L373
- https://github.com/Gompei/slack-rust/blob/main/src/socket/event.rs#L23-L35
- https://github.com/nascentxyz/pyrometer/blob/master/crates/graph/src/solvers/atoms.rs#L331-L332
- https://github.com/privacy-scaling-explorations/mpz/blob/dev/crates/mpz-circuits/src/types.rs#L88-L93
It seems to me that there is at least a decent number of rust users, including in some high-profile crates, that consider the Foo { .. } pattern to be at least worth using.
Is there a way to programmatically find uses of such a pattern?
I'm inclined to say that having the intuition of "if I can match it with a pattern, then I can create the value with a similar syntax" is good
This general guideline is nice, but it doesn’t really apply here. Foo { .. } to me does not suggest "this is how you make a Foo", but instead "I am deliberately omitting all details of how to make a Foo". And if a struct has private fields (the exact case this RFC is meant to address), you won’t be directly creating any values of it, with any syntax.
rust's semver story is quite bad
So we should work on making it better. Every little improvement will save some people some amount of pain when upgrading. This doesn’t have to be all-or-nothing.
This general guideline is nice, but it doesn’t really apply here.
Foo { .. }to me does not suggest "this is how you make aFoo", but instead "I am deliberately omitting all details of how to make aFoo".
Right, my brain is already trained to think of it that way because of tuple structs. It's like "I'm telling the computer that I am specifying a type and not declaring a binding".
For many years you could write (x: Type) in a pattern on nightly. I think that was never implemented fully and ended up being removed but sounds like this is the main reason people write Struct { .. } patterns for private structs today.
For many years you could write
(x: Type)in a pattern on nightly. I think that was never implemented fully and ended up being removed but sounds like this is the main reason people writeStruct { .. }patterns for private structs today.
this feature is called type ascription.