The Problem
A trait
that is generic over its container type has type specification requirements - users of the trait
must specify all of its generic types.
In the example below, the Contains
trait
allows the use of the generic types A
and B
. The trait is then implemented for the Container
type, specifying i32
for A
and B
so that it can be used with fn difference()
.
Because Contains
is generic, we are forced to explicitly state all of the generic types for fn difference()
. In practice, we want a way to express that A
and B
are determined by the input C
. As you will see in the next section, associated types provide exactly that capability.
struct Container(i32, i32);// A trait which checks if 2 items are stored inside of container.// Also retrieves first or last value.trait Contains<A, B> {fn contains(&self, _: &A, _: &B) -> bool; // Explicitly requires `A` and `B`.fn first(&self) -> i32; // Doesn't explicitly require `A` or `B`.fn last(&self) -> i32; // Doesn't explicitly require `A` or `B`.}impl Contains<i32, i32> for Container {// True if the numbers stored are equal.fn contains(&self, number_1: &i32, number_2: &i32) -> bool {(&self.0 == number_1) && (&self.1 == number_2)}// Grab the first number.fn first(&self) -> i32 { self.0 }// Grab the last number.fn last(&self) -> i32 { self.1 }}// `C` contains `A` and `B`. In light of that, having to express `A` and// `B` again is a nuisance.fn difference<A, B, C>(container: &C) -> i32 whereC: Contains<A, B> {container.last() - container.first()}fn main() {let number_1 = 3;let number_2 = 10;let container = Container(number_1, number_2);println!("Does container contain {} and {}: {}",&number_1, &number_2,container.contains(&number_1, &number_2));println!("First number: {}", container.first());println!("Last number: {}", container.last());println!("The difference is: {}", difference(&container));}