- In the previous lesson,
we looked at a trait called PartialOrd
where you can specify how to compare two values.
It turns out there's another trait called Ord,
which inherits from it.
So PartialOrd is the super trait
and then inheriting from it,
we have the Ord trait like so,
and what the Ord trait gives you on top of partial ordering
is a concept called total ordering, total ordering.
It's quite a tricky mathematical concept.
It's explained to you in the Wiki site if you're interested.
Let's take a look.
This is the Wiki site which explains total ordering
and it does get quite mathematical
if a number type like I don't know,
employee or bank account.
If it implements total ordering,
then it supports all of these kind of equivalences
and then there's more maths down here.
So if your maths is kind of okay, then you can read
through this and it'll be quite interesting.
You don't have to understand all of this detail
in order to use the Ord trait.
It would help, but it's not essential by any means.
So the Ord trait from a Rust point of view,
well, first of all, it does inherit from PartialOrd, okay?
So it'll inherit comparison capabilities
from the PartialOrd trait.
So it'll inherit the partial_comp function
to compare two items to see less than or greater than.
It inherits the less than test from PartialOrd,
less than or equal to,
greater than and greater than or equal to.
So all of those capabilities
are already inherited from PartialOrd.
I guess the question is what extra do we get
when we inherit from Ord?
So the Ord trait specifies these additional methods.
So you you would say I want to implement PartialOrd,
so Ord to compare my type against some other type.
The other type on the right-hand side is assumed
to be the same type as you.
So you'd have two objects
of the same type being compared basically.
And what you do, you have to implement the comp function.
It takes an instance of you
and an instance of the other object
and it returns one of these Ordering enums.
Remember that from before?
The Ordering enum basically indicates
whether you are less than, equal
or greater than the other object.
So you'd implement logic in here
to be doing either less than equal or greater than.
And as well as that,
once you've told it how to compare two values,
the Rust compiler will automatically implement
by default the maximum, minimum and clamp function.
Those functions are implemented by default based
on the algorithm you specify in cmp.
So the way you'd call the max function, for example,
we should say object a.max, object b like so.
The a object gets passed into self
and the b object gets passed in
as the other parameter like that.
And what you do is return an instance of the same type.
So whatever your type is.
Either a or b will get returned.
Either a copy of the value a
or a copy of the value b will be returned,
whichever is the larger.
And the min function obviously works
in a similar kind of way,
except that it returns the minimum of two values.
And then we have this interesting function,
we've got a clamp.
It's an interesting name for it.
And basically what it does,
it bounds a value within a upper and lower bound.
So you could say something like this, you could say a.clamp
and then you give it a max, a minimum value that you'd allow
for a and a maximum value that you'd allow for a like so.
If you passed in, it's like a number range.
You can imagine that minA, minA
and maxA are here.
If you pass in a number for A,
which is kind of between the minimum and maximum,
it'll just return that value.
If you pass in a value smaller than the minimum,
then it'll return the minimum
and if you pass in a value greater than the maximum,
then it'll return the maximum.
So it'll give you back a value which is guaranteed
to be clamped inside that range.
So it's quite an elegant concept
and that's implemented for you automatically
once you've told it how to compare two items.
It can figure that out for itself.
So the easiest way to implement the Ord trait
is automatically via the derive mechanism.
Now, there are some other traits
you've gotta implement as well.
If you implement Ord,
you also have to implement PartialOrd Eq and PartialEq.
And again, in most cases, you'd use automatic derivation
to implement those traits, like I've done here.
I've got a simple structure type, ExamMark
and I want to support
like the minimum, maximum, clamping mechanism for Ord.
I want to support the less than, greater than,
greater than, equal to,
less than or equal to from PartialOrd.
And I want to support comparability
via the equality and partial equality and all of those,
as long as your type here understands comparison,
like ints do, then you can just get the compiler
to generate these capabilities for you,
plus any other traits that you might also want
to implement, like debug.
Right, So let's say I've, on the previous slide,
my ExamMark structure implements the the Ord trait.
Then you can call the min function.
I've created some ExamMarks here.
I can say ExamMark1.min, ExamMark2.
M1 was 90, m2 is 99.
So this will return the smaller,
it'll return an ExamMark set to be 90.
If I call the max function,
it'll return a copy of the larger ExamMark.
That'll be 99.
Clamp, m3 is out of range here.
I want to clamp m3 to be
this is the minimum ExamMark I'm going to allow.
You can't get less than zero
no matter how badly you try in an exam.
And you can't get more than 100.
So the minimum is zero and the maximum value is 100.
If you pass in the value greater than the maximum,
it'll just return the maximum, 100.
If you pass in a value less than the minimum,
then it'll return you back the minimum value.
So the value that you get back would be clamped
to be within the range of zero to 100.
So that's actually quite nice.
Let's see an example in our demo project.
We run the main code and then we'll look at demo_ord
to see this in practice.
Okay, let's go.
Here's my main code and we are right down here now.
Demo_ord, there it is, and here's the code.
I've got a simple structure.
It is really nice.
Once you get into the kind of the ethos of Rust,
you start appreciating its elegance.
The compiler will implement minimum, maximum clamp.
It'll implement the comparability, less than,
equal, less than or equal to, greater than or equal to.
It'll implement equality tests.
Oh, I'm going to allow my structure type
to be copyable and clonable.
Remember, the copy trait inherits from clone.
So if you implement the copy trait
so that the value gets copied, not moved,
you also have to implement clone.
Oh and debug as well, just for good measure.
All of that, no charge, completely free,
"No charge," as one proton said to another neutron.
Anyway, there are my ExamMarks and m1.min m2,
that's courtesy of the Ord trait
and it'll give me back the maximum
and then clamp values between the minimum zero and 100
and the minimum zero and 100 there.
So we just need to run this and have a look at the results.
Okay then, so what are we gonna get?
The minimum of m1 and m2 should be 90 and it is 90.
The maximum of m1 and m2 should be 99 and it is 99.
If m3 is too large, when I clamp it
between the ExamMark zero and the ExamMark 100,
it'll clamp to be 100, inclusive notice.
And if m4 is less than the minimum,
when I clamp it between zero and 100,
it'll gimme the minimum value zero like so.
So very nice indeed.
This automatic derivation mechanism is what you use most
of the time, like I said, because the types,
when you have a numeric type,
the data in here will be something like integer or float
and they already understand these capabilities.
So the compiler's capable of generating all
of this stuff automatically for you
without you having to do any work at all.
You just use the functionality and it works.