- So far in this lesson,
we've seen how to declare variables.
We've seen integers, floats, characters, and Booleans.
And to wrap up the lesson,
we're going to look at some additional techniques
to help you write your code
more like a Rust developer would.
So some additional techniques which are quite useful,
some of them are a bit surprising,
but all of 'em interesting.
So first of all, we have this concept of inferred types.
When you declare a variable, if you give it a value,
then you don't also need to specify the type.
The compiler can guess the type
based on the value that you provide.
And this is the way that most Rust developers would do it.
So here you can see I've declared A, B, and C.
The compiler, I've given each one a value
and the compiler can guess the types
without me having to specify.
It'll guess that a is an i32, I think,
and it'll guess that b is a F64.
I think that's what it would be.
And C will be a character.
So I don't actually need to say let a colon i32.
I can just give it the value
and it can deduce the type from that value.
This is the way that most Rust developers write their code.
You can also declare variables to be mutable or unmutable.
Surprisingly, if you're coming from a C background
or C Plus Plus or Java, in Rust,
when you declare a variable like I've done here,
by default it's immutable, okay,
which means you can't change it.
There's been a drift in languages
in recent years towards this concept.
Immutable variables are safer than ones that can change.
If you know that a value is immutable, then for example
in a multi-threaded application, you don't need to lock it.
You can't change the value.
So you don't need to lock it when you are accessing it
because it's fixed.
So Rust is really about safety and code integrity.
That's the main selling point for Rust.
So when you declare a variable,
then it is by default immutable.
You can't change the value.
Once you've assigned it. That's it.
If you do want to have a variable that's mutable
then you have to use the mut keyword.
Looks a bit odd, but you do get used to it.
I declared e as a mutable integer.
I've given that initial value of zero,
but then I could change value later on okay?
So you have to explicitly request mutability
if you want it.
If you don't say mut, then it's immutable
by default for safety.
Here's another curiosity.
If you're writing some code,
and you've declared a variable,
but you haven't yet used it, you would normally
get a compiler warning to say
there's an unused variable here.
Did you forget to write some code to use it?
If it's intentional that you haven't yet written the code
but you will, then you can prefix the variable name
with an underscore, okay?
And any variable that starts with an underscore,
you won't get a warning from the compiler
about unused variables okay?
So there we go.
That stops you getting lots of warnings
about unused variables.
Just prefix the name with an underscore.
Okay that's quite straightforward enough.
Now there's the as keyword.
The as keyword is a type conversion.
Rust doesn't do any type conversions on its own.
If you want to convert an integer to a float
or a float to an integer,
you've gotta do that explicitly
using a type conversion using the as keyword okay?
So here in this example g is a floating point value
and I want to basically take g and convert it into
an i32, convert the value into an i32
and that integer value is then assigned to h.
Okay so you have to use the as keyword to do
any type conversions in Rust.
It doesn't do any type conversions unless
you say using the as keyword.
And again, it's for code predictability and code safety.
It only converts when you tell it to using the as keyword.
Now this next one's a bit of a curiosity.
Imagine you ask the user to enter a string, a number,
let's say from the keyboard.
They type in a number 1, 2, 3, 4, 5.
It comes into your application as a string.
You wanna convert it to an integer.
So you might do, there's an pauseint function
that we'll talk about later on in the course,
but you can actually use the same variable name.
So in this example initially I've got a variable called num,
which is a string,
and then maybe I convert it into a number
but then I reassign it back
to a variable with the same name.
I've re-declared the num variable.
It was a string initially,
but from this point onwards, it'll be an integer.
All right, so that's quite surprising.
I think for most people,
the fact that you can reuse a variable name,
because in most other languages you couldn't.
For example, in Java,
if you declared the first variable as a string,
you might declare a variable called num as a str,
and give it a string value, 1, 2, 3, 4, 5, like that.
And then you do some code to convert it to an integer.
And then the integer value, you would then have to assign
in other languages to a variable
with a different name like num int.
Okay, so initially you invented this name for the variable
and then you invented this name for the second variable.
And what Rust says is, or what you can say in Rust is
you know, really it's the same concept.
Initially, I'm going to express the number in string format
but from here onwards, I want to express it as an integer.
So from here onwards, it'll use the num as an integer.
You don't have to invent different names for it.
You can recycle the name and then use the new variable,
the integer variable here afterwards, okay?
You can still use it, the kind of old style approach
if you like giving it variable names,
which are kind of different
or you can use the same variable name
if you prefer as supported in Rust.
Right one final thing, I'm gonna show you a demo after this.
You can have compile-time constants.
A value which is known at compile time
doesn't need to be evaluated at runtime.
So therefore slightly more efficient,
because there's no runtime code involved.
In this example, use the const keyword.
Rust is quite fussy.
When you have a const keyword, you basically
have to use capital letters for the variable name.
It's quite fussy about names.
And you've also gotta specify
the type of the variable as well.
Okay you could argue that it can guess
it can see that seconds in hour
I've given it the value 3,600.
It can see that it's an i32,
but you have to explicitly tell it.
Again, I think it's just for predictable code,
so that it doesn't get the wrong idea.
Okay and by the way the underscore here
doesn't have any kind of real meaning.
It's just there as a developer nicety
to make it clearer, easier how to read the number 3,600.
The underscore doesn't actually have any real meaning.
It's just there for us programmers to read more easily.
Let's have a look at an example.
Same project as before, lesson two variables types.
And it's the demo additional type function
we're gonna look at.
Then we'll run the example as usual.
Okay, so here's my code, and in the main function
I'm going to uncomment a call to that function.
And here is the function down here.
It's basically got bits of code similar
to what we looked at in the slide, okay?
So I'm gonna run through that in a moment.
I'll run it first of all, so we can actually see the output.
I think it'd be beneficial to be able
to see the output first,
and then we can kind of go through the code
as we're discussing, cargo run.
So it gave me a few warnings about unused functions,
same as before and ignore those.
And then we declared some variables of inferred type.
So an integer, a float, and a character.
I've got some new line characters
being displayed as well here
just to make my output look nice.
So it outputs the value of A, B, and C.
No great surprises there.
Those are the values of A, B, and C as expected.
And then here I've got a variable,
which is immutable by default.
If I try to change it, I'm gonna get a compiler error.
Now, if I try to run the application,
line 80 is gonna gimme an error.
The warnings or the errors that you get in Rust
are actually quite useful.
It'll tell you there's an error on line 80, count the five.
So here we are, here's line 80,
but it tells you why the error occurred.
It says, well, in the beginning you declared the variable
and it wasn't mutable.
Maybe it should have been
because then you tried to reassign it, the value.
Okay so it'll tell you what caused the error in blue,
and then it tells you what the error actually is in red.
So that's actually really useful.
It tells you how to fix the error as well.
The wording of the error message is quite useful.
Quite suggestive, cannot assign twice to immutable variable.
So when you declare an immutable variable,
you don't have to initialize it all at once.
You can declare it on one statement
and then assign the value on the next statement.
So that actually is okay.
It's just that you can't assign it the value twice.
So as it stands now, if I rebuild and rerun it,
it'll actually be okay, I'm not gonna get any errors now.
I declared it as immutable.
You are allowed to assign it the value once somewhere,
not necessarily immediately,
but you're not allowed to then reassign it the value again.
So if I then try to do this, okay,
then the first assignment would be, okay,
it's gotta have some value.
The second assignment is the problem.
So now I'm gonna get an error again.
Okay, so we're kind of back to square one.
So let me just revert my code to as it was,
and I'll just kind of get rid of that statement.
If you want to have a variable that is mutable,
then you've gotta explicitly say mutable like so.
Okay, so originally e will be zero.
Afterwards, e will be one.
Let's just verify what could possibly go wrong.
Cargo run.
Okay, so e was originally zero and afterwards e is one.
Excellent what you might be tempted to do
if you are familiar with C Plus Plus
or almost any other language
you might be tempted to say use the plus plus
operator either as a postfix or as a prefix.
Rust doesn't have a plus plus operator.
It doesn't have a minus minus operator either.
So I'm gonna get an error when I try to build and run.
Plus plus is not a valid operator.
It says prefix here.
It's not valid as a postfix operator either.
It suggests using the plus equals instead.
That's quite helpful.
So you could, if you wanted an increment by one,
you could say e plus equals one,
or you could obviously plus equals two,
or you could multiply equals two.
That would be the same
as saying e equals e multiplied by two okay?
So whatever you wanna do
you can rotate it as much as you want now.
Here I've got a variable f,
which I might use later on, but I haven't used yet.
The underscore will stop the compiler
from giving me a warning.
If I got rid of the underscore,
I'd get a compiler warning and use local variable.
Okay, and what else?
Conversions. So g was 3.99.
I convert the value 3.99 into an integer
and then I output the value of h.
So let's see, what is the value of g
and what is the value of h?
Let's give that a spin.
So g is 3.99 and h is three.
And again I think that's probably what you'd expect,
isn't it when you take a fluid point number
and convert it to an end.
It doesn't round up, it just truncates, okay?
It kind of goes downwards towards negative infinity.
So 3.99 becomes three when you convert it into an integer
as you can see down here.
And then, so here's my num as a string.
Okay, so num as a string is there,
and then I've repurposed the num keyword.
So that num is now an integer okay?
So now I can do maths on it.
Okay, now it's an integer.
I can now treat it as an integer and I can do maths on it.
I can add one.
And then finally I've got a const,
the number of seconds in an hour.
you have to tell it as an integer 3,600.
That's a compiled time constant.
So is this, this is a compile time constant,
even though I've done a bit of maths,
it's all completely doable at compile time.
So the compiler will effectively do the multiplication,
and it'll tell me that the number of seconds in a day,
if there are that many seconds in an hour,
and there are 24 hours in a day, then the number
of seconds in a day will be that number there okay?
So the techniques that I've shown you
in this section here are important.
Rust developers tend to use type inference.
Rust developers tend to declare variables as immutable
wherever they can,
and they only declare mutable where they have to.
And you have to make an effort like that.
Using underscores to avoid warnings.
Most organizations have a rule
that you're not allowed to run code that has warnings.
So this would get rid of warnings.
Type conversions.
You do when you have to.
It always feels a little bit unclean,
but sometimes you do have to convert
from one type to another using the as keyword.
And then recycling variable names.
You don't tend to do that much,
but it's useful when you need to use it.
And then compile time constants,
obviously they give you readability.
It's much better to have a compile time constant
to represent a number like here, seconds in day.
I could have hard coded that as being 86,400.
But anybody reading my code will think
what on earth is that number?
Is that your daily salary?
Sadly not.
It's the number of seconds in a day,
but using the name of the variable
or the name of the constant makes my code clearer.
Okay, so that wraps up this lesson.
Good to go.