Below a MWE. Is it possible to define a function with generic, such that the as casting of the Enums is still possible within the function?
Perhaps I could implement From/Into, but the as cast is preferable here for it's simplicity.
Is there a way I could tell the compiler that the generic input is of "primitive" type that can be as cast?
enum Blue {
A=4,
B=42,
C=1
}
fn demo_blue(input: Blue) -> usize {
input as usize
}
enum Orange {
A=42,
B=1
}
fn demo_orange(input: Orange) -> usize {
input as usize
}
// this does not work
fn demo_both<T>(input: T) -> usize {
input as usize
}
fn main() {
println!("{}", demo_blue(Blue::B));
println!("{}", demo_orange(Orange::A));
println!("{}", demo_both(Orange::A));
}
Into and From are the traits appropriate to generalized conversion, and, as you discovered, there is no inherent impl Into<usize> for enums - you have to provide one.
Won''t From/Into have some overhead over the primitive numeric as cast? At least the MIR on playground shows a difference to the mere as cast
Follow up: If I were to consider tuples (Orange, usize) that I want to cast to usize by as casting the enum and multiplying by the usize in the 2nd position of the tuple, then the From/Into trait solution won't work?
In a release build, they produce identical code. There may be differences at other optimization levels, though. Of course the specifics of translation are never entirely guaranteed, but Rust's compiler team makes a point of issues like this, so I would expect this to remain true in the future even if it is only an informal property.
Due to the rules around trait coherence, you can't implement other libraries' traits (including traits from core or std) for other libraries' types (including types defined by core, std, or the language itself). Therefore, you can't add a From impl to (T1, T2), even if one of the types T1 or T2 is local to your crate.
What you can do is define your own tuple struct:
struct ScaledOrange(Orange, usize);
impl From<ScaledOrange> for usize {
fn from(value: ScaledOrange) -> usize {
let ScaledOrange(orange, scale) = value;
orange as usize * scale
}
}
Or, more likely, you can define a named operation rather than relying on From for this. From is appropriate when there is a single, contextually-"obvious" conversion that is universally applicable to a pair of types, but if the conversion requires domain knowledge or if it would only be appropriate some of the time, then using a normal, named operation, rather than From, is usually more legible.