Skip to content
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

RFC: Statics in patterns #3305

Closed
Closed
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 266 additions & 0 deletions text/3305-static-in-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
- Feature Name: `static_in_pattern`
- Start Date: 2022-08-17
- RFC PR: [rust-lang/rfcs#3305](https://github.com/rust-lang/rfcs/pull/3305)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary

[summary]: #summary

Allow referencing non-`mut` `static`s in pattern matches wherever referencing a `const` of the same type would be allowed.

# Motivation

[motivation]: #motivation

Rust pattern matches compare a scrutinee against compile-time information. Rust generally doesn't allow patterns to depend on runtime information; that is relegated to match guards. However, Rust currently also prevents patterns from depending on *link-time* information, specifically statics from `extern` blocks.

I encountered this restriction while trying to port the Rust standard library to [cosmopolitan libc](https://justine.lol/cosmopolitan/index.html). Cosmopolitan provides an API that mostly matches POSIX, with one major exception: constants like `ENOSYS` and `EINVAL`, which on most platforms are defined as C `#define`s (equivalent to Rust `const`s), are instead provided as C `const`s (equivalent to Rust non-`mut` `static`s).
Jules-Bertholet marked this conversation as resolved.
Show resolved Hide resolved

```rust
// libc crate

cfg_if! {
if #[cfg(target_env = "cosmopolitan")] {
extern "C" {
pub static EINVAL: i32;
pub static ENOSYS: i32;
pub static ENOENT: i32;
}
} else {
pub const EINVAL: i32 = 42;
pub const ENOSYS: i32 = 43;
pub const ENOENT: i32 = 44;
}
}

// stdlib code

use libc::*;

fn process_error(error_code: i32) {
match error_code {
// Compiler throws error EO530 on Cosmopolitan,
// because `static`s can't be used in patterns, only `const`s
EINVAL => do_stuff(),
ENOSYS => panic!("oh noes"),
ENOENT => make_it_work(),
_ => do_different_stuff(),
}
}
```

Because Rust patterns don't support statics, all the `match` expressions in the standard library that refer to POSIX constants would currently need to be rewritten to accommodate Cosmopolitan.

```rust
// stdlib code adapted for cosmopolitan

use libc::*;

fn process_error(error_code: i32) {
if error_code == EINVAL {
do_stuff();
} else if error_code == ENOSYS {
panic!("oh noes");
} else if error_code == ENOENT {
make_it_work();
} else {
do_different_stuff();
}
}
```

Needless to say, this is unlikely to ever be upstreamed. Allowing statics in patterns would solve this use-case much more cleanly.

# Guide-level explanation

[guide-level-explanation]: #guide-level-explanation

Rust patterns can refer to constants.

```rust
const EVERYTHING: i32 = 42;

fn foo(scrutinee: i32) {
match scrutinee {
EVERYTHING => println!("have all of it"),
_ => println!("need moar"),
}
}
```

With this feature, they can refer to statics as well.

```rust
static EVERYTHING: i32 = 42;

fn foo(scrutinee: i32) {
match scrutinee {
EVERYTHING => println!("have all of it"),
_ => println!("need moar"),
}
}
```

Mutable statics are not allowed, however. Patterns can't reference information that can change at runtime, and also can't be `unsafe`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring the unsafe issue, is there a reason that the pattern can't reference information that can change at runtime? My understanding of how cosmopolitan is going to use this is that it references information that changes at runtime (but only once, before main), the values of the statics are not available at link time like they normally are. Given that why would it not be possible for a developer to do the same thing using static mut where they set the value once early in main before the pattern is ever tested against.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason that the pattern can't reference information that can change at runtime?

The "patterns can't reference information that can change at runtime" rule is not something that I am proposing, it's a rule that Rust already has and that I see no reason to change. Tearing down this Chesterton's Fence may or may not be a good idea, but is out of scope for this RFC.

My understanding of how cosmopolitan is going to use this is that it references information that changes at runtime (but only once, before main)

This is where the exact definition of "at runtime" comes into play. If you define "runtime" as "the time when Rust code runs", then the values of the statics don't change at runtime. I argue that for the purposes of this feature, it doesn't really matter whether the values of the statics are set by the linker, the operating system, the C runtime, etc; all of these things fall into the common category of "rustc doesn't know what the value is, but Rust code can rely on the value being constant." Note that Cosmopolitan sets the values of the statics in assembly (that I don't presume to fully understand); C code written against Cosmopolitan sees them as standard C extern consts, and doesn't need to worry about their value changing, either.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fundamentally this is how all rust static values work. Officially Rust "doesn't have life before main", but it still requires someone (an OS or an assembly boot script) to initialize all the pre-conditions before Rust code actually begins executing.

So this part of the RFC is basically nothing new.

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet Aug 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel a good analogy is dynamic linking. When you call a dynamically linked function in Rust, the compiler doesn't know the address of that function; that only gets determined when the program runs. But you can still write a safe wrapper for the dynamically linked function, and then treat it pretty much like any other Rust function (though not like a const fn, of course).

Extern statics are the same. Unlike consts, rustc doesn't know their value. But, once the necessary #[trusted_static] ceremonies are performed, it should still be possible (IMO) to treat them"pretty much" like any other constant value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fundamentally this is how all rust static values work.

Not normally, the vast majority of Rust code is statically linked which means the values are known at link time (and LTO would then be able to make const and static patterns generate the same code).

Extern statics are the same. Unlike consts, rustc doesn't know their value. But, once the necessary #[trusted_static] ceremonies are performed, it should still be possible (IMO) to treat them"pretty much" like any other constant value.

Would it be unsound to use #[trusted_static] on a dynamically linked extern static that gets initialized early-in-main before any code that reads the static runs? It just seems like it should be possible to unsafely promise that a value is constant once it starts being used in patterns, given that is essentially the same behavior you get with dynamically linked statics (where the dynamic loader has unsafely promised to the executable that all statics have been initialized and will no longer be written to).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh whoops, I'm the one in charge of trusted statics? That kinda stalled out because of like, long term correctness discussion XD

uhm, don't expect that one soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That kinda stalled out because of like, long term correctness discussion

Is there a list of the unresolved questions somewhere? (Or does one need to be compiled?) Might it be possible to ship an obviously correct MVP sooner, and consider loosening safety requirements later?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just the zulip thread, which the project tracking issue should link to.

The problem is really simple: It's hard to tell people that the unsafe-adjacent code they've been writing the past 6 years is just barely wrong, and that they need to start doing everything the opposite way because of small edge cases. That's a hard sell.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to tell people that the unsafe-adjacent code they've been writing the past 6 years is just barely wrong, and that they need to start doing everything the opposite way because of small edge cases.

Could we figure out the shiny new Right Way of Doing Things ® now, worry about migrating/accommodating old code later once people are used to the new syntax? The longer we wait before offering better tools, the more technically-bad code gets written, and the more painful any necessary changes will be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's easy if migration of code and re-training of programmers isn't a concern ;3

Literally we just would have to say one day "hey declaring extern static/fn is unsafe now". And like, a compiler patch to enforce that. That's the entire end goal for Rust 3015. It's just the migration that needs to be designed really.


```rust

static mut EVERYTHING: i32 = 42;

fn foo(scrutinee: i32) {
match scrutinee {
// ERROR can't refer to mutable statics in patterns
/* EVERYTHING => println!("have all of it"), */
_ => println!("need moar"),
}
}
```

Statics from `extern` blocks are allowed, but they must be marked as trusted using the (not-yet-implemented) [trusted external statics](https://github.com/rust-lang/lang-team/issues/149) feature.

```rust
extern "C" {
#[unsafe(trusted_extern)]
static EVERYTHING: i32;
}

fn foo(scrutinee: i32) {
match scrutinee {
EVERYTHING => println!("have all of it"),
_ => println!("need moar"),
}
}
```

# Reference-level explanation

[reference-level-explanation]: #reference-level-explanation

For a static to be eligible for use in a pattern, it must:
Copy link
Member

@RalfJung RalfJung Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if this is enough for soundness. The reason const are not allowed to even point to immutable static is pattern matching soundness: if you have a const of type &i32, we better make sure that the integer it points to is truly immutable. But if you define a static X: &i32 by doing &mut MUTABLE_STATIC and then transmuting that, and then you do const C: &i32 = X, now you have yourself a potentially unsound pattern.

But for extern static we don't know their values, so we can't know if they do shenanigans like that. Also for all we know their type may be a lie, so maybe they actually do get mutated? There is an entire lion's den of issues here that we have so far worked very hard to avoid (e.g. grep the compiler source code for can_access_statics and read all the comments associated with it), and all of this work is for naught if we allow extern static in patterns.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I hope we can fix this in a future edition

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this needs a new edition, we just need to be okay with semver hazards and post-monomorphization errors.

But anyway, that won't help for extern static. I can see some ways of resolving the structural match situation that would probably also make other kinds of "opaque consts" like extern static be fine (*), but as of now I don't know where the structural match stuff will go so I'd rather not rely on it going any particular way.

(*) Well, there are also the issues around "evading match arms" by changing the pattern mid-evaluation, but that can probably be avoided by not doing any exhaustiveness checking and clearly documenting when opaque patterns vs valtree patterns are used (because they will probably not always be equivalent).

Copy link
Contributor

@Lokathor Lokathor Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant specifically that with extern static (non-mut) I would like declaring them to be the unsafe part, and it would require precise type and truly immutable memory and such, then the accesses themselves can be the safe part and people can worry a lot less.

Doesn't help the actual valtree matching (or not) part of it though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition we need here is that any pointer we reach through this memory points to immutable memory, recursively. This is required for matching on references. It is why I keep saying this is a bigger soundness issue than the RFC lets on.

I doubt trusted statics want to require that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but if that value can be valtree'd, then those two things are not UB-equivalent.

How? The current stuctural match rules oly allow constants (and, with this feature, statics) if PartialEq::eq is guaranteed to give the same results as a valtree-based match.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mutating the value referenced by a &&&i32 is itself UB imho,

That's probably not correct. You certainly shouldn't rely on it. The rules for UB on references will probably not recursively follow references to arbitrary depth, but instead work on a 'shallow' level -- mutation is only UB one level down.

As a concrete example, Miri sees no UB here.

How? The current stuctural match rules oly allow constants (and, with this feature, statics) if PartialEq::eq is guaranteed to give the same results as a valtree-based match.

The current rules are a mess and before anyone relies on them for anything, we need to re-do those rules. See rust-lang/rust#74446.

The future rules will, hopefully, give users the ability to unsafe impl StructuralEq for MyType. (And in fact they can already do that, even without unsafe, by using unstable features.)

Users of course cannot expect things to behave completely sanely when they do that, but we still need to specify precisely what happens. Note in particular that saying "it is UB to have such an instance" does not work, that's simply not how we are using UB in Rust -- UB can only arise as a consequence of the program executing a statement in the wrong way, and only under conditions that can actually be decided automatically. "This type has a PartialEq that behavesd equivalently to valtrees" is not a decidable question (halting problem, yada yada), and hence cannot be grounds for UB.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"This type has a PartialEq that behaves equivalently to valtrees" is not a decidable question

But "at the point this specific match statement was executed, this specific call to PartialEq::eq completed and returned a value without panicking, and that return value was different from what a valtree-based match would have returned" is decidable, no?

(This would mean loop {} is a valid PartialEq::eq impl, but I don't think that's a problem).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mutating the value referenced by a &&&i32 is itself UB imho,
That's probably not correct. You certainly shouldn't rely on it.

All the issues that arise with "a static of type &&&i32 could change during a pattern match" already occur with "a scrutinee of type &&&i32 could change during a pattern match", no?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But "at the point this specific match statement was executed, this specific call to PartialEq::eq completed and returned a value without panicking, and that return value was different from what a valtree-based match would have returned" is decidable, no?

So now we need a way to express that in MIR, and carefully specify exactly what that does and when.

That might be possible, but it'll be a lot of work, and it'll also make the core language spec more complicated for a niche feature.

Instead I think we should just specify that statics are treated as opaque for pattern matching and always use PartialEq no matter their type. We don't have any other "UB due to valtree inconsistencies", and IMO there is no good reason to introduce such UB here.

All the issues that arise with "a static of type &&&i32 could change during a pattern match" already occur with "a scrutinee of type &&&i32 could change during a pattern match", no?

No, patterns and scrutinees are treated very differently in pattern matching, because the pattern is inherently known at compile-time and the scrutinee isn't. This is not a symmetric situation. This RFC is proposing to make it more symmetric, but that is exactly why this is a bug fundamental change to pattern matching.

See here for a long discussion about how to ensure that we have UB when the scrutinee changes during pattern matching. The same approach does not obviously work for patterns, since it relies heavily on the fact that we compare the same value X against a number of things -- Rust has some ingredients that make it possible to ensure that X doesn't change when it is used multiple times. But with patterns that just happen to use the same extern static in several places across several patterns, we have none of that structure, so it'd be even more complicated.

So, again, we should just specify that extern statics are treated as opaque, and we should make no claims that this is equivalent to a const of the same type and value.


- not be marked `mut`
- not be marked `#[thread_local]`
- not come from an extern block, unless it is marked as safe to use with the [trusted external statics](https://github.com/rust-lang/lang-team/issues/149) feature
- have a type that satisfies the structural match rules, as described in [RFC 1445](1445-restrict-constants-in-patterns.md), but without any allowances for backward compatibility like there are for consts (e.g., floating point numbers in patterns) . These rules exclude all statics with interior mutability.

Static patterns match exactly when a const pattern with a const of the same type and value would match.
Copy link
Member

@RalfJung RalfJung Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very subtle guarantee, since the way these patterns are treated will have to be completely different. Matching consts relies heavily on knowing their value. const will be taken apart to a 'valtree' that is matched on field-by-field, as least if the type permits, whereas extern static will necessarily be compared via PartialEq. This may or may not produce the same result, depending on how PartialEq is implemented.

Above you reference the structural match RFC, but the rules in that RFC have little to do with what the compiler actually does. The real rules are quite the mess and need to be cleaned up, see rust-lang/rust#74446. But it seems inevitable that at least in unsafe code it will be possible to have "structural match" types that actually violate the structural match contract, and we need to say what happens then. This is not an operational requirement, so it probably cannot become UB.

So, I think guaranteeing they behave exactly the same is not feasible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If future relaxations of the structural match requirements are somehow impossible to port to static matches, we can always require the stricter rules for static patterns while loosening them everywhere else. It's impossible to say more without knowing what such relaxations might look like.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Future relaxation" makes it sound like we currently have some rules that we understand, and we want to change them. That is not the case. As I said, what the compiler does and what the RFC says are already different, and last time this came up, there was little interest in adjusting the compiler to the spec -- rather, the spec will be adjusted to the compiler.

So, you simply cannot use that RFC as a basis for further language evolution.

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet Aug 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I referred to the structural match RFC, I was mostly concerned about the guarantee (as of right now) that, for any local x and const Y of the same type, match x { Y => true, _ => false } evaluates to true iff PartialEq::eq(&x, &Y) evaluates to true. Do you know of a counter-example to this that works in stable Rust?

Copy link
Member

@RalfJung RalfJung Aug 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have patterns where PartialEq::eq(&x, &Y) doesn't even compile, see e.g. this example from the issue that I linked twice already (rust-lang/rust#74446).

Also your stated equivalence is insufficient to substantiate the claim in the RFC since it fails to take into account UB. There are some fundamental reasoning principles for patterns that are currently true, that are no longer true with this RFC -- as explained here.

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet Aug 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have patterns where PartialEq::eq(&x, &Y) doesn't even compile

OK, I definitely need to account for that then. But are there cases where it does compile, but gives a different answer?

(Also, is "comparing function pointers is weird" just a potentially-fixable LLVM quirk, or is there some deep reason behind it?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I definitely need to account for that then. But are there cases where it does compile, but gives a different answer?

No, I don't think so. But I already explained why that is not sufficient grounds to claim that static and const matching are equivalent.

(Also, is "comparing function pointers is wierd" just a potentially-fixable LLVM quirk, or is there some deep reason behind it?)

It's because of unnamed_addr, see e.g. rust-lang/rust#70861


The values of statics are treated as opaque for reachability and exhaustiveness analysis.

```rust
static TRUE: bool = true;
static FALSE: bool = false;

fn foo(scrutinee: bool) {
match scrutinee {
TRUE | FALSE => println!("bar"),

// The compiler will throw an error if you remove this branch;
// it is not allowed to look into the values of the statics
// to determine that it is unreachable.
_ => println!("baz"),
}
}
```

As an exception, when all safe values of a type are structurally equal, the compiler is allowed to see that the match will always succeed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"all safe values" is definitely impossible to implement. I could define

// Safety invariant: the field is always 0.
struct MyWeirdUnit(i32);

MyWeirdUnit has only one safe value, but the compiler has no way of knowing that.

Maybe you meant "all valid values", but even that seems way too subtle to include here. Do we have any precedence for a rule like that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, "is allowed to see" is too weak; I assume you mean that the compiler must accept the code below, not just that it is allowed to accept the code below.

Copy link
Member

@RalfJung RalfJung Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And finally, given that this is a large fraction of the RFC, there also needs to be motivation for all this complexity caused by "1-valued types". The motivating example certainly doesn't need any of this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I basically interpreted this as exclusively applying to ZSTs, but wanting to be more general in case we somehow added the ability to increase the number of cases we could detect this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, is isn't possible now, but it eventually could be possible to mark fields as ignored when deriving Eq, which would allow such a type to be non-zero-sized while still having a zero-sized equivalence class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this should be "valid". I had a reason in my head for why this should be "safe", but upon writing it out I realized that my logic made no sense.

As stated in the RFC, there's no practical use-case (AFAIK) for the "one valid value" rule. It's just that, on general principle, I feel the compiler should never force you to write useless code that it statically knows to be unreachable, unless there's a good justification for it.


```rust
// Not all `&()` are bitwise equal,
// but they are structurally equal,
// that is what matters.
static ONE_TRUE_VALUE: &() = &();

fn foo(scrutinee: &()) {
match scrutinee {
ONE_TRUE_VALUE => println!("only one branch"),
// No need for a wildcard.
// The above match always succeeds.
}
}
```

Visibility and `#[non_exhaustive]` can affect whether the compiler can tell that all values of the type are structurally equal.

```rust
mod stuff {
#[derive(PartialEq, Eq)]
pub(super) struct PrivateZst(());

pub(super) static PRIVATE_ZST: PrivateZst = PrivateZst(());
}

fn foo(scrutinee: stuff::PrivateZst) {
match scrutinee {
stuff::PRIVATE_ZST => println!("secrets abound"),
// `stuff::PrivateZst` has a field that's not visible in this scope,
// so we can't tell that all values are equivalent.
// The wildcard branch is required.
_ => println!("incorrect password"),
}
}
```

```rust
// crate `stuff`
#[derive(PartialEq, Eq)]
#[non_exhaustive]
pub struct PrivateZst();

pub static PRIVATE_ZST: PrivateZst = PrivateZst();

// main crate
extern crate stuff;

fn foo(scrutinee: stuff::PrivateZst) {
match scrutinee {
stuff::PRIVATE_ZST => println!("secrets abound"),
// `stuff::PrivateZst` is marked `#[non_exhaustive]`
// and comes from an external crate,
// so we can't tell that all values are equivalent.
// The wildcard branch is required.
_ => println!("incorrect password"),
}
}
```

The examples above all use `match`, but statics would be allowed in all other language constructs that use patterns, including `let`, `if let`, and function parameters. However, as statics cannot be used in const contexts, static patterns are be unavailable there as well.

# Drawbacks

[drawbacks]: #drawbacks

This change slightly weakens the rule that patterns can only rely on compile-time information. In addition, static patterns may have slightly worse performance than the equivalent constant patterns.

# Rationale and alternatives
oli-obk marked this conversation as resolved.
Show resolved Hide resolved

[rationale-and-alternatives]: #rationale-and-alternatives

The proposed rules around reachability and exhaustiveness checking are designed to ensure that changing the value of a static, or changing from a static defined in Rust to a trusted extern static, is never a breaking change. The special dispensations for types with a single value could be considered unnecessary, as matching on such a type is a pointless operation. However, the rules are not difficult to implement (I managed to do it myself, despite near-zero experience contributing to the compiler), and are arguably the most correct and least surprising semantics.

Allowing unsafe-to-access statics in patterns (`static mut`s, untrusted `extern` statics, `#[thread_local]` statics) is another possibility. However, I believe this option to be unwise:

- Rust generally has not allowed unsafe operations (like union field accesses) in pattern matches
- It's not clear where the `unsafe` keyword would go (within the pattern? around the whole `match` or `let`? what about patterns in function parameters?)
- it requires Rust to commit to and document, and users to understand, when exactly it is allowed to dereference the static when performing a pattern match

As for not making this change at all, I believe this would be a loss for the language as it would lock out the use-cases described above. This is a very simple feature, it doesn't conflict with any other potential extensions, the behavior and syntax fit well with the rest of the language, and it is immediately understandable to anyone who is already familiar with matching on `const`s.

# Prior art

[prior-art]: #prior-art

As far as I am aware, no other language has an analogous feature. C's `switch` statement does not allow referring to C `const`s.

# Unresolved questions

[unresolved-questions]: #unresolved-questions

The motivation for this RFC assumes that [trusted external statics](https://github.com/rust-lang/lang-team/issues/149) will eventually be implemented and stabilized. Other than that, there are no unresolved questions that I am aware of.

# Future possibilities

[future-possibilities]: #future-possibilities

None; this is a very simple and self-contained feature. I've argued against some possible extensions in the [rationale and alternatives](#rationale-and-alternatives) section. Future changes to the structural equality rules might affect this feature, but that is anther discussion and out of scope for this RFC.