A problem about Trait methods

Here is persudo codes:

trait GetNum<T> {
    fn get() -> f32 or i32;
}

struct S1;
struct S2;

impl GetNum<S1> for S1 {
    fn get() -> i32 { 1 }
}
impl GetNum<S2> for S2 {
    fn get() -> f32 { 1.0 }
}

My target is :

  1. S1 and S2 both is impented on GetNum;
  2. S1.get() return i32 and S2.get() return f32.

I guess enum can works fine, but is too verbose. Is there other ways I can do that ?

Maybe this is what you want?

trait GetNum<T> {
    fn get() -> T;
}

struct S1;
struct S2;

impl GetNum<i32> for S1 {
    fn get() -> i32 { 1 }
}
impl GetNum<f32> for S2 {
    fn get() -> f32 { 1.0 }
}
1 Like

Yes, It is, thanks. I just also try it out.

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.

6 Likes

Thanks much for your reply with your exerience, it is very useful for beginers like me.

One more question, if I use associated types instead of type parameters, how can I get to this:

struct S3;

impl S3 {
    fn get(&self, s: impl GetNum) -> ??? {
        s.get()
    }
}

In using type parameters, maybe I can get to that like this:

struct S3;

impl S3 {
    fn get(&self, s: impl GetNum<T, N>) -> N {
        s.get()
    }
}

I find a way to do this in this page:

Then I can do this:

struct S3;

impl S3 {
    fn get<G: GetNum>(&self, s: &G) ->  G::Output {
        s.get()
    }
}

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.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.