Note that there is an alternative solution, using associated types instead of type parameters:
trait GetNum {
type Output;
fn get() -> Self::Output;
}
struct S1;
struct S2;
impl GetNum for S1 {
type Output = i32;
fn get() -> Self::Output { 1 }
}
impl GetNum for S2 {
type Output = f32;
fn get() -> Self::Output { 1.0 }
}
Whether you want to use this or the one above depends on your use case. In short:
The use of associated types removes the generic type parameter, which can make your life considerably easier when writing code that is heavy on type-level computation. (The reason for this is that retrieving the context from which to get a sensible type parameter for your trait might be difficult, but this will come to you naturally with experience.)
However, the use of generic parameters allows you to
implement multiple versions of the trait for the same type (eg. you can implement GetNum<i32> and GetNum<u64> for S1), which the solution based on associated types doesn't support.
Basically, an associated type is a type-level function which maps one type (the implementor) to exactly one other type (the associated type), since that's what functions do – they can't be multi-valued. In contrast, the approach based on a type parameter really has a parameter, just like functions have – and you can choose any number of them to implement essentially different traits for the same receiver type.
My advice would be: start with associated types, and if you find out you potentially need to implement many versions of your trait for the same type, then switch to a generic type parameter.
Yeah, don't use impl Trait in argument position, it has limitations (artificial and technical alike) that make it a pain. Use impl Trait as a return type only.