Is this ever the right decision though?
In my specific use case, I want to go through one code path when a variant change, and another when it wasn't but the fields inside the variant may have possibly changed. If someone uses my function with a non-enum type, and the variant will be constant, they'll always get the second behavior - which is probably not what they expect.
I don't know if it's a representative for the common use case for discriminant
, but I'd imagine that if someone ends up using this function with a non-enum it's because they've made a mistake (probably a cascading one - passing a non-enum directly to it would have been easy to spot). And the fact that its behavior for non-enums is currently unspecified supports this opinion (better than undefined, but still not something one should use). I thought I'll have to work hard to construct an example for this pitfall, but it turned out simpler than anticipated:
use std::collections::HashSet;
use std::mem::discriminant;
#[derive(Clone)]
enum Shape {
Circle(f32),
Rectangle(f32, f32),
Triangle(f32, f32, f32),
}
fn count_different<T>(items: impl IntoIterator<Item = T>) -> usize {
items
.into_iter()
.map(|item| discriminant(&item))
.collect::<HashSet<_>>()
.len()
}
fn main() {
let shapes = &[
Shape::Circle(1.0),
Shape::Rectangle(2.0, 3.0),
Shape::Triangle(4.0, 5.0, 6.0),
Shape::Circle(7.0),
];
println!("Wrong answer: {}", count_different(shapes));
println!("Right answer: {}", count_different(shapes.to_owned()));
}
count_different
is only meaningful when T
is an enum. But when I used count_different(shapes)
, T
was a reference to an enum - so discriminant
gave a constant value and it did not do its job.
Should a reference to an enum be treated like an enum or like a struct? I say neither - the Rusty standards dictate that such code should be rejected, and that the compiler errors will guide me to write the correct code that does what I meant to do.