- [Instructor] The Rust library makes
extensive use of generics.
It defines generic structures, generic enums,
such as option and result.
Generic traits and generic functions.
And you can also define your own generic types and functions
in your own code,
and that's what we're going to do in this lesson.
So first of all,
you can define a generic structure like this.
Quite straightforward, similar syntax to many languages.
I define coordinate structure
which holds X, Y, Z coordinates,
but I haven't limited myself
to the type of those values.
I haven't said X and Y and Z are integers or floats.
I've made it generic.
This is a type parameter, T.
X, Y, and Z will be of some type T, okay?
So I don't care.
They could be integers,
they could be floats, I don't care.
It's the same kind of concept.
You can use any type name, not just T.
Generics can have multiple type parameters,
and we'll have a look at that later on.
Like hash map for example, has a key type and a value type.
You just specify multiple types
inside the angle brackets with a comma,
K,V, that kind of syntax.
Right, so it's easy to define a generic structure.
When you instantiate the generic structure
the compiler has to know
what type T is going to be.
One way to do this
is to specify the type parameter explicitly
when you mention coordinate,
and I've done that here.
I've explicitly said, I'm creating a coordinate
where T is i32,
and then obviously I have to give i32 values
for the X, Y, and Z parameters, okay?
So that's explicit typing,
the compiler with bis and instantiate a coordinate of i32
with i32 fields here, here and here.
However, most of the time
you can actually allow a compiler to guess
what the type is going to be
based on the values you supply.
So this syntax would also work.
In the first example,
I explicitly said, T is going to be i32,
but in this case the compiler can guess.
It knows that X, Y and Z will be of type T.
It can see the types of parameters are passed in
so it'll understand that T implicitly is a float.
So X, Y and Z,
probably gonna be float 64 would be my guess.
So in that case, it's actually kind of better in a way
to not specify the type parameter here
and just leave the compiler, guess for itself
what the types are.
I mean if you want to,
you can specify type explicitly,
but in most cases you'll just fall back on implicit typing.
The compiler can guess
what type to instantiate for T here.
Right, well, very simple that, actually.
Let's have a look at an example.
For this lesson, the project is lesson13_generics.
So here it is in the rustdev folder, lesson13_generics.
So let's open that in the code editor
and here we are.
Okay, so we have kind of a simpler structure
than we looked at in the previous few demos.
I've got a main, main file here
which just pulls in various other functions in other files.
Okay, so first off,
we should be looking at a demo of generic structures.
Let's uncomment that statement
and we'll come into it here, demo_generic_structs.
So I've defined a private structure,
just for this module
with private fields, just for this module.
T is a type parameter,
whatever type you wanna call it, T is fine.
First of all, a coordinate, C1 is a coordinate
where T is explicitly a type parameter
is explicitly passed in i32 for T.
So X, Y, and Z will be off type i32 explicitly.
And then using the implicit syntax
where the compiler can guess
that the types are going to be float 64.
And no need for us to specify explicitly the type parameter.
Oh, one thing I will mention,
syntax that might be a bit different
from what you're expecting.
If you are explicitly specifying the type parameter
you have to use a double colon
between the original structure
and the type parameter inside angle brackets.
Okay, and that's probably different syntax
from what you're expecting from other languages.
So for example, in Java and other languages,
you'd specify just that.
You'd say, "I want to coordinate of i32,"
but that syntax doesn't work in Rust.
You've gotta put a double colon in the middle.
So that's gonna look a bit odd
if you did get used to initially
but it is required.
The coordinate structure is a generic,
and then you have to put the type parameter
after the colons like so.
Or of course, you can just use implicit type in
like this, okay?
so I'll run the application
just to make sure that's gonna work okay.
And then I'm gonna try something else out
just to see what happens
if I pass in different types for the parameters
for the properties for X, Y and Z.
So first of all, it initializes outputs
the coordinate C1 is integer based.
Yes, indeed.
And then coordinate to C2 uses floating point
numbers 1.1, 2.2, 3.3.
Okay, that's good.
I wonder what would happen
if I actually passed in different types.
So I passed in, they're meant to be the same type, okay?
And the compiler can guess here
that they're float 64, I guess.
What would happen if I passed in an integer
and an integer and a floating point value?
I wonder what would happen then.
What do you think would happen in that case?
Let's see.
Oof, so remember, Rust doesn't do implicit type conversions.
It's kind of confused here.
It saw these as being integers,
therefore it expected Z also to be an integer
and it found a floating point number
and okay, so what about the other way around?
What if it was 1.1, 2.2, and then 3?
Okay, so float, float, integer.
I wonder what would happen now, do you know?
Let's have a look.
Again, an error.
It says, right, so if X is a float
and Y is a float
and they're all meant to be the same type
then surely Z has to be a float as well.
And it isn't a float, it's an integer.
Now you might think, well,
a compiler worth its salt would promote the integer
to a float and we'd get away with it.
Rust doesn't do that.
The Rust is very rigid when it comes to types.
It doesn't do type conversions.
Even simple types like in to float.
So even though it could
in theory convert that to a 3.0 doesn't, it says, right
well hang on Andy, what are you doing?
I was expecting a float to point number
and I found an integer.
So you've gotta be consistent.
If these things really are the same type, then you
really do need to give it the same type everywhere, like so.
So hopefully now it'll work again and we can rest in peace.
There we go.
Okay, so defining generic structures
straightforward enough.
Use T instead of a actual type name.
And then to instantiate
either explicitly specify the type parameter or
leave the compiler guess based on the values it sees.