[No Audio]
Associated types in Rust are similar to
generic types. However, associated types and
limit the types of things a user can do,
which consequently facilitates code
management. They are related to the idea of
type family, in other words, grouping
multiple types together. That description is
a bit abstract, so let's dive into it right
now. I want to calculate how far my car will
travel in three hours if it's going at a
certain speed. I'm going to define a couple
of structs for this purpose, with the names of
Kmh, that is kilometer per hour, and Km,
that is kilometers, each containing a single
field of u32, and we'll derive the Debug.
[No Audio]
Defining structs with single fields like this
is a great way to protect and safeguard
against programming errors. Now it's
impossible to accidentally compare Kmh, that
is kilometer per hour against Km or kilometers.
[No Audio]
This is fine so far, but in some
places, people are more familiar with miles
per hour and miles, instead of kilometer per
hours and kilometers. To cater for those
people, we will also need to include
implementation for the miles and miles per
hour. So let me include a couple of more
structs for the Miles per hour and Miles,
having the similar syntax to that of Kmh and Km.
[No Audio]
Next, I am interested in determining how far
my car has gone in three hours. So, let us
first implement this four kilometer per hour
inside an implementation block.
[No Audio]
The input to the function will be self, and the
output will be the distance in kilometers, so
it will be of type Km.
[No Audio]
Inside the function, we will return kilometers
with a value being equal to the speed
multiplied by three.
[No Audio]
This means that the distance covered in three
hours will be the speed in kilometer per hour
times three. Please note that self.value
in this case, will be the speed since self
represents the kilometer per hour instance.
In the same way, we will add a function for
computing the distance in three hours to the
implementation block of miles per hour.
[No Audio]
The definition of the function will be
similar to the above function, but in this
case, it will return miles instead of kilometers.
[No Audio]
Let us use these functions in the main. I will
declare a variable of speed having a type of
Kmh or kilometer per hour with a value of 90.
[No Audio]
Next, I will declare another variable which
will compute the distance in three hours by
calling the function.
[No Audio]
Finally, I will display the two variables
in a print statement.
[No Audio]
I will use the same three lines for the miles
per hour now. So I will copy and paste, and
we'll make changes in the variable names.
[No Audio]
Let us cargo run this.
[No Audio]
The code works and is
doing our job. It feels a bit weird that we
have these two almost mirror
implementations without unifying them, it
seems like a perfect use case for a trade.
Let us look closely at the implementation of
the Kmh and Mph. You may note that both
implementations share the function of
distance in three hours, and both the
function returns a distance, although having
different types. This means that, there is
something which is shared among the two
types. As I already covered in the course,
that we use traits for sharing behavior among
the types. Let me comment out these
implementations and define a trait called a
DistanceThreeHours.
[No Audio]
I will include a single function in this
trait called, DistanceThreeHours.
[No Audio]
The input will be self, what about the output
now? Should it be kilometers or miles? If I
restrict it to kilometers, then the
implementation for the Kmh
will have no issue, but we will run into
issues when we do implementation of the same
for Mph. There must be some unifying type
here, which based on the implementation will
assume different types. Here comes the
concept of associated types. I will declare
a new associated type called distance before
the function name. The output of this function
will now be of type Distance. The Distance is
now a new trait item. This type is associated
with the type we are defining the trait for.
We refer to it in the body of the trait
definition via Self::Distance,
therefore the term self here. Let us now
implement the same trait for the Kmh.
We will first include the implementation block.
[No Audio]
Next, we will declare the
distance to be of type Kilometers.
[No Audio]
Next, we will declare the function of
distance in three hours with the input of
Self and the output of Self::Distance.
Inside the function, we will return Self::Distance,
and the value will be set to that
of self.value times three.
[No Audio]
In the same way, we can go ahead and
implement the distance in three hours for the
Miles, I will first into the implementation block.
[No Audio]
Next, I will copy the code above, and we'll
paste it, and now I will just change the
kilometers to that of Miles.
[No Audio]
In the first implementation, which is for the Kmh,
by declaring that type distance
equals to Km, we are basically saying
that, that the distance type associated with a
kilometer per hour is kilometer. And in the
same way, in the second implementation block
by declaring the type Distance = Miles.
We are basically saying that, the
Distance type associated with the miles per
hour is Miles. This means that different
implementation blocks attaches different
meanings to the distance, and therefore allows
for having some flexibility, and a way of
organizing our code more effectively. Inside
the main function, you may note that the
distance variable in the first case has been
automatically assigned the correct type of
kilometers, and in the second case, it has
been automatically assigned the type of Miles
which we intend to do. That is it for this
particular tutorial. Do come back again and
until then, happy Rust programming.
[No Audio]