I have to admit that this is a really wired (and maybe unclear) question, so let me explain it further with two examples adapted from Rust By Example.
In the Example 1, we have a const value in the first arm and ranges in the second and third arms of match
, which actually implies comparability of a type (in this case i32
). And we know that to express comparability in Rust, we need to impl
traits like PartialOrd
, Eq
etc.
So, a sub-question is when we write pattern matching like Example 1, do we need to care about the traits like PartialOrd
etc.? From another perspective, how can we do pattern matching like Example 1 using our own types?
// 1
fn main() {
println!("Tell me what type of person you are");
match 15 {
0 => println!("I haven't celebrated my first birthday yet"),
n @ 1 ..= 12 => println!("I'm a child of age {:?}", n),
n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n),
n => println!("I'm an old person of age {:?}", n),
}
}
The above questions come up actually when I looked at Example 2. For temperature, we don't really need an enum
but a float number, since the IS unit of temperature is Kelvin after all. If I want a type Temperature
, I will make it comparable and I definitely would like to do pattern matching like in Example 1 (yeah.... floating point imprecision is too annoying to have full comparability).
// 2
enum Temperature {
Celsius(i32),
Farenheit(i32),
}
fn main() {
let temperature = Temperature::Celsius(35);
match temperature {
Temperature::Celsius(t) if t > 30 => println!("{}C is above 30 Celsius", t),
Temperature::Celsius(t) => println!("{}C is below 30 Celsius", t),
Temperature::Farenheit(t) if t > 86 => println!("{}F is above 86 Farenheit", t),
Temperature::Farenheit(t) => println!("{}F is below 86 Farenheit", t),
}
}
Following Example 2, if I want guarding and ranging in pattern matching for my Temperature
type, can I do that? If I can, do I need to impl
traits like PartialOrd
?
Below is my idealized code, please bear with me.
pub struct Temperature(u32); // u32 in Kelvin, just for demonstration purpose, bear with me please
const THRESHOLD_HOT: Temperature = Temperature::celsius(40); // suppose we have const fn celsius()
const THRESHOLD_COLD: Temperature = Temperature:: farenheit(40); // suppose we have const fn farenheit()
// any impls for Temperature needed??
fn main() {
let t = Temperature::celsius(35);
match t {
hot_t if temperature > THRESHOLD_HOT => println!("TOO HOT!"),
acceptable_t @ THRESHOLD_COLD .. THRESHOLD_HOT / 2 => println!("acceptable"),
comfy_t @ THRESHOLD_HOT / 2 ..= THRESHOLD_HOT => println!("comfortable"),
cold_t if temperature < THRESHOLD_COLD => println!("TOO COLD!"),
}
}
The example in Destructuring array/slice is also interesting if you want to somehow destructuring your own array types.
All in all, I guess the conclusive question will be: Can we fit pattern matching into Rust type system?
I check the Patterns chapter of Rust reference and it says limited types are allowed in range pattern, so I guess there's been some thinking, but I don't know whether there's an RFC or something like that.
If you know something related, please let me know. Thanks!