static ZERO: i32 = 0;
fn whee(x: i32) {
match x {
ZERO => println!("x is zero"),
_ => println!("x is not zero"),
}
}
Are there any reasons not to accept this code, with the ZERO pattern behaving as if ZERO was a const? Note that UnsafeCell does not implement StructuralEq, so interior mutable statics could not be used this way.
Motivation
I'm trying to build the Rust standard library for cosmopolitan libc. Cosmopolitan provides an interface that mostly matches POSIX, with one exception: many constants like ENOSYS are defined instead as statics that are initialized once before main to the proper platform-specific value. The Rust stdlib matches on these constants in many places; rewriting all those match statements would be a large maintenance burden.
Given this motivation, you are effectively needing match to be able to equality match its input against a value only known at runtime. (the static). This would have very different codegen from the status quo, and implications on things like match exhaustiveness or branch reachability. It feels strange to think that rust might add support for this, but only for statics and not for e.g. locals. (the only possible justification I can think of is that one could say that it is "path expressions" which are allowed to be used in a match)
When the static is defined by Rust, the compiler does know its value at compile-time; there should be difference between this csse and const in terms of exhaustiveness or reachability checking in patterns. When the static is declared inside an extern block, as in my motivating example, the Rust compiler doesn't know its value at compile time, and therefore can't use it to inform reachability or exhaustiveness checks. However, by the time any Rust code is running at runtime, its value will already have been fixed. If you define "constant" as "value won't change at runtime", an extern-block static is conceptually a constant value as well.
(Also, allowing patterns to refer to the values of locals would be a breaking change. This AFAIK is not)
This solution makes me sad because it would mean rewriting much of the standard library to accommodate my niche targetβand I doubt the Rust libs team would want something like that merged upstream. Maybe my use case is just too niche; but on the other hand, if the implementation is not too complex, I see no reason not to allow this feature.
Patterns are compile time information matched against a runtime value. Making patterns depend on runtime information (even information that is guaranteed not to change once main is called) is a significant change to how pattern matching works. I think you'd probably need an actual RFC to hash out all the details of how that would actually work.
That being said, I totally get why you don't want to change all of those match blocks in the standard library. It may be worth asking the libs team for their input on solutions that are likeliest to get upstreamed.
If proc-macros are available to std, just use a cfg_attr(xxx_platform, rewrite_macro) on it, and rewrite the code pattern to the usable one inside the macro, will do all the magic, right?
An alternative approach could be to have std import the constants as constants (for example, Linux's constants), and have the errno or cvt functions convert between the platform values and the Linux values. This of course won't fix any user code which tries to match against ENOSYS for example, but neither would rewriting the matches. I don't know how this would work for non-errno constants.
std and rustc can't use standard procedural macros because of bootstrapping issues. Instead, they use directly implemented proc-macro-likes which are written against the compiler-internal types rather than the proc_macro bridge types.