Otobo
Otobo

Reputation: 764

Matching a generic parameter to another generic parameter of an impl

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?

Upvotes: 2

Views: 645

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65752

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.

Upvotes: 1

Related Questions