2

I have this code (in playground):

trait Limit {}

pub trait Trait
{
    fn give<T>(&self, x: T) -> T
    where T: Limit;
}

struct Struct<T: Limit> {field: T}

impl<T> Trait for Struct<T>
    where T: Limit
{   
    fn give<S>(&self, x: S) -> S 
    where S: Limit
    {
       self.field
       //interacts with x parameter and gives an "S: Limit" result       
    }
} 

What I want to do is to keep the signature of give function of the trait Trait and at the same time to implement the trait Trait for a the generic struct Struct.

but I get this error

<anon>:17:8: 17:14 error: mismatched types:
 expected `S`,
    found `T`
(expected type parameter,
    found a different type parameter) [E0308]
<anon>:17        self.field       
                 ^~~~~~

I thought to use what I saw in this question which matches an associated parameter with a generic parameter so I changed:

    fn give<S>(&self, x: S) -> S 
    where S: Limit

to:

    fn give<S = T>(&self, x: S) -> S 
    where S: Limit

I didn't get an error about this syntax but it wasn't the solution of the error above.

Is there any way to achieve what I want to do?

And a side question, what <S = T> actually does in this case?

Community
  • 1
  • 1
Otobo
  • 714
  • 1
  • 7
  • 13

1 Answers1

1

As you wrote it, your implementation of Trait must implement give in a way that works for any type that the caller wishes. On the other hand, your implementation of give for Struct<T> only works for a specific type, T.

What about making the trait itself generic, rather than the method?

pub trait Trait<T> where T: Limit {
    fn give(&self, x: T) -> T;
}

impl<T> Trait<T> for Struct<T> where T: Limit {   
    fn give(&self, x: T) -> T 
    {
       self.field // does not compile, you can't give away one of your fields
                  // unless you mem::replace() it with another value
    }
} 

This way, an implementation of Trait<T> only works for a specific T type that is chosen by the implementor, not the caller.

Another option is to use associated types instead:

pub trait Trait {
    type T: Limit;

    fn give(&self, x: Self::T) -> Self::T;
}

impl<T> Trait for Struct<T> where T: Limit {
    type T = T;

    fn give(&self, x: T) -> T 
    {
       self.field
    }
} 

Here, Trait is no longer generic, but Struct remains generic, and each instance of Struct implements the same Trait trait.

Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • Thanks for the answer, yes this would be a solution, however implementing this changes the signature of `give` function. To be honest what I want is not using generics on trait `Trait` as this leads to using generics in other traits that depend on it which I want to avoid. – Otobo Sep 05 '15 at 07:49
  • Finally, In my case using your suggestion in combination with declaring the generic of trait `Trait` as `Self` when I define other depending traits solved the issue. An example of defining other trait depending on `Trait`: `trait NewTrait: Trait {...}` However I'll wait for another solution, just in case someone else can not use `Self` in trait definitions. – Otobo Sep 05 '15 at 12:30
  • I've added another solution, using associated types. – Francis Gagné Sep 05 '15 at 19:17
  • Yeah that is a nice work around, the only issue is that if you are going to implement `NewTrait` and `Trait` for a type that doesn't use generics bounded by `Limit` then you have to use `PhantomData` in order to keep the associated type generic. This `PhantomData` case gets similar to using `Trait`, at least in appearance. Anyway, it seems that there is no other way, so thank you one more time for your answer. – Otobo Sep 10 '15 at 21:09