Disambiguating Const Pattern Match

Currently, if you write code like:

const SPECIAL: u64 = 10;
struct X {
field: u64
}

let a: X = X{ field: 100 };
match a {
    X{field: SPECIAL} => panic!("Should Not Be Reached");
    X{field: NOTSPECIAL} => println!("Reached");
}

It will do what you expect it to. However, what's bizzarre about this is that if the constant falls out of scope, the pattern matching breaks. E.g...

mod did_a_refactor {
   const SPECIAL: u64 = 10;
}

will cause the code to panic.

Is there any way I can rewrite my match to look like:

match a {
    X{field: %SPECIAL} => panic!("Should Not Be Reached");
    X{field: NOTSPECIAL} => println!("Reached");
}

So the compiler knows %SPECIAL should always be a constant?

When a const is in scope the pattern matches based on the const value just like you'd typed the value of the const in the pattern. Otherwise the identifier is interpreted as a binding which binds to the value of match scrutinee.

For example in

match Some(1usize) {
    Some(x) => assert_eq!(x, 1usize),
    None => panic!()
}

The x is a local binding to that match arm, so you can use the value inside the Some variant.

rustc warns you that your pattern bindings should be lowercase, because bindings are expected to have lower case names while consts and statics are expected to be in all caps. It isn't immediately obvious as an indicator of what's happened, but there is at least something there in the diagnostics.

Yep, I'm aware of the current behavior of the compiler, I think what I was more hoping for is for a way to make code more resilient against the "thing in scope" pattern matching.

#[deny(non_snake_case)]
match a {
    X{field: SPECIAL} => panic!("Should Not Be Reached"),
    X{field: NOTSPECIAL} => println!("Reached"),
}

Works, though it obviously relies on your codebase being otherwise consistent in how things are named

I appreciate your help; I think it'd be better if there were a first-class feature which supported this, less so my particular issue...

Maybe this is a better fit for the internals forum?

Yeah, I'm not of aware of any way to force an identifier in a pattern to be interpreted as a const and error if it isn't one. I agree that it's unfortunate there isn't an obvious way to disambiguate.

If nobody else chimes in with a solution, internals would definitely be the place to go to discuss adding it to the language

Just specify it with a path.

If it is at the crate root:

const SPECIAL: u64 = 10;

struct X {
    field: u64,
}

fn main() {
    let a: X = X { field: 100 };
    match a {
        X {
            field: crate::SPECIAL,
        } => panic!("Should Not Be Reached"),
        X { field: NOTSPECIAL } => println!("Reached"),
    }
}

Otherwise you could put it in a module just for constants, or part of an impl block for a struct. Anything like that will work and prevent it from matching as a pattern binding.

2 Likes

I believe inline_const can be used for it.

yeah that seems like inline const is exactly what I'm looking for, short of that the fully qualified name trick seems to work as well (tho i worry about tools like clippy/fmt suggesting/changing it to an import).

I wouldn't worry about that. I often import just a module name, and evey item I use from that module gets the module name prefixed in front. Clippy has no issues with this, and I find it lets me use shorter names that would otherwise practically need the module name as part of the item name anyway.

Instead of:

use foo::FOOBAR;
let x = FOOBAR;

Do:

use foo;
let x = foo::BAR;