- So far in this lesson, we've seen how to use arrays
in tuples, both of which are fixed size,
once you've created them, you can't expand them.
If you want to have a resizable collection
then you might want to use vector
or Vec as it's called.
Vec angled bracket T,
it's a generic, sequential, resizable collection.
So it's generic in the sense
that you have to say what type of element it contains,
it'll contain elements of the same type.
It's sequential like an array, starts at element zero
but it is resizable, which is unlike an array.
The Vec structure is defined in the Vec module,
which is part of the standard crate in the standard library.
Okay and this module, std vec is automatically imported
and available in your code,
so you don't need to have any kind of use statement,
you can just use the Vec structure directly
to create vectors.
So there were two different syntaxes to create a vector,
one way or the other the vector needs to know
what type of element it contains.
So when you declare your variable, you can use either
of these syntaxes to give it that information.
In the first example, I declared a mutable vector, V,
and it's of type Vec i32.
Okay, so it will contain 32 bit integers
and then you use this syntax
to actually create the vector itself,
okay, it'll be empty initially, but it'll be resizable.
So technically speaking, Vec is a structure
and it has what we could call a static method.
It creates a vector which is empty initially,
it will eventually contain 32 bit integers.
This is an alternative syntax
which you might prefer or maybe you prefer the first,
you declare the variable
and then you use the type information here,
you say, V is a vector of 32 bit integers,
create a new instance of that type,
and that'll give you back an empty vector
into which you can insert elements.
There is another way of creating a vec,
which people will probably use in practice,
and it looks like this.
There's a macro, a bit like print lin.
There's a macro called Vec exclamation mark, or rather
the macros called Vec and with a lowercase V, notice.
And what this macro does it effectively, it inserts code
to create the vector like this and to insert these elements
into the vector so it creates the vector
and populates it immediately.
And if you declare the vector as multiple,
then you can change it again afterwards if you want to.
Okay. So either of these approaches work.
Create an empty vector if you don't know the data yet
or create a vector and populate it with the data
if you do know what you want to put in.
So to index into a vector, no great surprise,
we use the square brackets.
The index here must be within range.
It does do bounce check-in
if the index is greater or equal to the length.
If you got a vector of 10
the last real element is nine.
So if the index is out of bounds, then your program crashes.
Technically it panics.
This is a term that you'll see in Rust.
A program panics,
it basically throws an exception effectively
or it raises an error that causes the program to terminate.
Okay. So when you see panic messages
it means the program has crashed
and the error would be indexed out of bounds.
So obviously you need to fix that.
If you're not sure,
if the index is within range, then check first.
This is an alternative approach.
You can use the get function
as a way of getting a value in a vector.
It returns an option.
Okay. So when you call the get function,
you give it the index
it'll return an option, and this option will
either contain some value if the index is okay
or it'll contain none if the index is invalid.
So what we would typically do then
is you would take this option and check to see
whether it contains a value or not.
You use the match statement like this.
So take the option that came back effectively
like a narrow wrapper.
Take the option it came back and test what it contained.
Does it contain some value?
In which case, give me the value, you know
basically the value at that index, we can the print.
If it contains none, really what it's saying is that
the index was out of bounds and it didn't crash the program
because that would be a little bit, hysterical.
It turned an option, which you can then check to see
if there was no value at that index.
Right, so vector is resizable.
It provides a large set of methods to allow you to insert
and append and to remove
and all kinds of things, and to check the length.
So the documentation for the vector structure is here.
Let's take a look at that.
Here we are.
This is the vector structure
in the Vec module, in the standard crate
and these are the bunch of methods
that are defined in vector.
So like any programming language
when you start using the collection classes
you need to understand the basic premise,
a resizable sequential collection.
And then once you've got the basic idea,
the rest of it is just the details
of what methods it contains.
Okay. So if you look through here,
the method names are actually quite well chosen
and quite intuitive I think,
which isn't true for all languages.
And you've got examples for each one.
So for example, there's a push function.
Let's have a look at some of these examples.
Push function, you give it an item and it appends it,
it puts it at the end
and the vector grows by one element.
The pop function I wonder what the pop function returns.
Let's have a look.
So the pop function removes the last element
from a vector and returns it, it returns an option.
Okay. I guess theoretically, well quite likely,
quite possibly the vector could be empty.
And if you try to pop an element from a empty vector
what could it possibly give you back?
There isn't any value it could give back.
So it does an option.
The option will contain some value
if the vector wasn't empty
or the option would be none if the vector was empty.
Okay. So that actually is quite a good example
of option in practice.
Lots of functions that could possibly return a value
or maybe not.
They wrap the value up in an option to mean
that possibly there may be no value here.
You need to check, you'd need to check
you'd have a a match statement to check
if the option contained a value or if it contained none.
So that actually is quite, quite useful I think.
And then the insert function you insert at a given index
the item you want to insert
and it'll move the other elements one place to the right.
Okay, to make a gap for the new element to be slotted in.
And well, obviously there's a length function as well.
How many elements does my vector contain?
So if the vector has 10 elements
they will be from element zero up to element nine.
Okay, you can iterate over the elements in the vector.
If I've got a vector called V
I can iterate over the items in the vector.
So the basic syntax looks quite straightforward
but notice the ampersand prefix on the vector variable.
We haven't seen that before
and it turns out to be a big deal.
This ampersand, it's called borrowing in Rust.
If you are a C plus plus developer,
you might call it a reference.
When you iterate over a vector,
you've gotta take a reference to the vector.
Basically the fall loop has a reference to the vector object
and then can refer to the elements inside it.
It is required to use the ampersand
when you iterate over a vector.
If you didn't, then the vector contents
will be emptied in the loop.
Okay. So as if you didn't have the ampersand in here,
as you iterate over the loop, the vector will gradually,
the elements in it will be removed during the iteration.
That's quite a complicated mechanism involved here.
It's to do with the ownership model
and borrowing in Rust and it's quite tricky.
So we're gonna have a look at all of that
in quite a lot of detail later.
I'm gonna explain how Rust manages the lifetime of objects
and how you can borrow objects without destroying them.
Quite tricky.
Very important.
We'll discuss it later, in detail.
Right. Well, luckily for us,
the vector type also implements the debug trait.
So in other words, it has a format function.
We can output a vector
using debug formatting in the print lin
you can output a vector object using curly bracket,
colon, question mark
and it will call the FMT, the format function on the vector
to display the continents of the vector in one go,
which is quite useful for debugging.
So let's have a look at the example,
same project as before,
lesson 05, compounds, collections.
We'll have a look at the demo vectors function.
Right. Well here we are.
So in my main code,
we've got some examples we've been looking at previously.
We had tuples we looked at earlier
and we had arrays we looked at early before that.
We're gonna look at vector now.
So let's have a look at demo vectors
and it kind of illustrates the various different techniques
we were looking at doing the PowerPoint slides.
Let's just run it so we can see the output
and then we can discuss as we go through.
Okie-dokie.
I tell you what I forgot to do.
I forgot to actually call the function.
It would help, wouldn't it?
Let's actually call the demo vectors function here,
a deliberate error there and the output is here.
Okay, so we're in the using vectors
that's my headline print statement, using vectors.
Yes, indeed.
So on the vector structure called the new method
it's a static method.
In fact, static methods
in Rust are called associated functions.
We'll come back to that later.
So this is one syntax
for creating a vector of 32 bit integers.
This is the alternative syntax.
Notice that these variables
are put in underscore at the front
'cause I'm not actually using those variables anywhere.
That was just for kind of syntax demonstration purposes.
This is the vector I'm gonna be using,
using the Vecmacro to both create the vector.
It knows what type of elements you've got here.
So it knows it's like a i32
and it'll create a a vector of i32
and push those three elements into it.
Okay. So my vector has three elements.
If I try to access an element within bounds
then that's fine.
It'll be turned back in i32 and 32 bit integer
and the value was a hundred.
At position zero was the value a hundred.
That's good.
I can call the get function if I'm not sure.
If I had an index and I wasn't sure if it was within range,
then get is a safer alternative
to just plow it straight in with the square brackets
returns an option, check that option,
does it contain some value?
In which case give me the value.
I'm getting element zero, so it should give me the value
a hundred again, which is indeed what it gives.
I've pushed some items.
So push means append.
So initially the vector contained a hundred, 101,
102 followed by 103, followed by 104, followed by 105.
It'll remove the last element.
So that's the end of element 105.
And then at the beginning it'll insert 99.
I wonder what the items look like after that.
Let's have a look.
Let's iterate through the vector items.
Remembering the borrowing syntax here, or reference
as you might call it in C plus plus
it is called borrowing in Rust.
So we should call it that, so that when you print
when I print my vector here
it prints each element on a separate line and it is here,
that looks correct to me.
That looks right.
And then finally we can use the debug formatter
to open the vector all in one go,
which is, probably more convenient.
And there it is.
Right. So just before we finish, let's see what happens.
If I try to get an element, which is out of range.
Let's say I try to get the element to position 42
obviously that's not valid.
So in this case
I would expect it to say no value.
Ready, home.
So let's give that a spin.
So there we go.
No value.
That's that output there.
So the get function fails gracefully, it returns an option
which may contain a value or not.
If you use the square brackets operator
then good luck with that.
You need to know for sure that the index is within range.
If it isn't, then the program will basically crash.
It'll terminate, it'll panic.
That's the Rust terminology.
It panics, it crashes with an error message
and the program terminates at that point.
So this will cause a program panic when I run it.
Okay, so the main thread in my application panicked.
In other words
it raised an error that caused the program to crash,
index out of bounds.
That's the name of the error.
Quite a useful error message.
The length of the vector is three, but the index is 42
and it was online 87 in my main code.
Okay, so that's a very comprehensive error message.
My program has terminated,
so there's no way back now, it's a terminal situation
so you'd need to go back
and fix the code so that it would then work properly.
So I'll fix the code, run it again
and this time it's going to work isn't it?
Beautiful.