const NAN: f32 = f32::NAN;
match f32_value {
// These constant patterns are allowed.
0.0 => (),
f32::INFINITY => (),
// error: cannot use NaN in patterns
// evaluates to `NaN`, which is not allowed in patterns
f32::NAN => (),
NAN => (),
_ => (),
}
NaN is never equal to any value, so I can see why matching against NaN is disallowed. That’s not my concern here; my question is what StructuralEq is and how it is implemented.
The page for StructuralPartialEq says that it is a "Required trait for constants used in pattern matches."
Does that mean the constant 0.0 implements StructuralEq but the constant f32::NAN does not? Is StructuralEq implemented per value rather than per type?
I'm so confused because the values appearing in the code are all the same type: f32. How can one f32 value implement StructuralEq while another does not?
Pattern matching is compiler magic, the exact semantics cannot be replicated in the surface language. As the documentation of StructuralEq says,
This is a hack to work around a limitation in our type system.
I actually thought that pattern matching floats is disallowed; it seems there was an intention to do just that back around 2017, but it was only ever a lint, and was removed in 2023 in favor of just making matching against NaNs a hard error. Floats don't implement StructuralEq, but that's fine because they're primitive types and have their own special semantics. The trait exists primarily to allow matching against constants of user-defined types (and, IIUC, to eventually support const generic parameters of user-defined types).
The pattern check is value-based. The relevant property for values is structural equality. The trait is one part of deciding if a value has structural equality, but not the whole story.
See here:
Note how the variant of an enum value is also taken into consideration (so a constNone::<T> is always usable).
After ensuring all conditions are met, the constant value is translated into a pattern, and now behaves exactly as-if that pattern had been written directly.
Does this mean the compiler compares the value and constant pattern in a field-by-field way like this? If so, what happens if a private field exists in the value? value.a == pattern.a && value.b == pattern.b && ...
Or does it compare at once like this? value == pattern
Or is #[derive(PartialEq)] required, but its implementation not actually used? If so, how is the comparison done?
Const patterns are compared for equality on a field-by-field basis, recursively, ignoring privacy. However, in stable rust, this will always have the same behavior with the PartialEq impl. (Except for a bug.)
I just searched for "Rust StructuralEq" on DuckDuckGo. I can confirm that it appears at the top of the results on both DuckDuckGo and Google in my environment.