Jump to content

Rust for the Novice Programmer/Arrays and Vectors

100% developed
From Wikibooks, open books for an open world

Arrays

[edit | edit source]

Now assume we wanted to store some numbers and find the median of them. As a stats 101 refresher, the median is the value in the centre if they are ordered. So as an example, out of 9, 3, 8, 2, 7 the median value is 7 since 2 and 3 are the lowest two, 8 and 9 are the highest two and 7 is in the middle. Now the question is how do we store the numbers? We could do something like this:

 fn main() {
     let number1 = 9;
     let number2 = 3;
     let number3 = 8;
     let number4 = 2;
     let number5 = 7;
 }

However, this has many downsides. We have to remember and use the variable names; we would have to refer to them directly and there's no easy way to go through every number without a manual process. That's why there is a built-in way to deal with these, called an array:

 fn main() {
     let numbers = [9, 3, 8, 2, 7];
 }

An array is a list of things, organised so you use the order in the list to refer to it. An important thing to note is that the first thing in the array is referred to as the 0th element and you get it by using square brackets. Also an array's type is [type of thing; number of things]. We can explicitly write it out as follows:

 fn main() {
     let numbers: [i32; 5] = [9, 3, 8, 2, 7];
     println!("{}", numbers[0]);
 }

This will print out 9 since it is the first element of numbers. Say we wanted to print all the elements in numbers, how could we do this? We can use a tool from the previous page, which is the for loop and ranges. To get the number of elements in numbers, we can use numbers.len(). This is a function on a type, so you can use it with a dot after any array to get its length. We do have the length above but if we change the number of elements, the .len() will still work correctly, so this helps to keep it easy to change. With all that, here's the way to loop through and print all the values in the array:

 fn main() {
     let numbers = [9, 3, 8, 2, 7];
     for index in 0..numbers.len() {
         println!("{}", numbers[index]);
     }
 }

The index is the name for the number of the array corresponding to the value inside. Using [] brackets to get a value is called indexing into the array. Note that because the array starts at 0, and because ranges are non-inclusive, index goes from 0 to 4 but not to 5. If we tried to run

 fn main() {
      let numbers = [9, 3, 8, 2, 7];
      println!("{}", numbers[5]);
 }

Our compiler complains that this operation will panic at runtime. Panicking is what it is referred to in Rust when something illegal is done, so the program just stops running. You never want to panic, so our compiler tries to help us out! The Rust compiler is quite smart and will help us out a lot.

So now we want to write a function to get the median of an array of values:

 fn get_median(input: [i32; 5]) -> i32 {
     //to do
 }

The most obvious way would be to sort the array so it is in order, then get the middle value. How do we sort an array? A simple method is to go through from left to right and left to right again and if a number on the right is larger to swap them. This is quite a weird concept to get your head around, so here's the code:

 fn get_median(input: [i32; 5]) -> i32 {
     let mut array = input;
     //sort array
     for index1 in 0..array.len() {
         for index2 in index1..array.len() {
             if array[index2] > array[index1] {
                 array.swap(index1, index2);
             }
         }
     }
     //to do, return median
 }

Note the for loop inside the other for loop. This is called a nested for loop and means for each value in the array, all the other values will be compared against it. Also the inner for loop's range starts at the index of the outer one. This is so we don't compare values that have already been compared, it's not strictly necessary but it means we don't do unnecessary steps. The array.swap() function is one like the .len() one earlier, but it takes two parameters which are indices(the plural of index) into the array and swaps those two values.
This whole thing is called an algorithm which is a series of steps to solve a problem. Sorting arrays is a classic algorithm since it is very common and this is quite a simple one.
Next, we should return the median of the array. The median is the centre point of the array, so we could just do array.len() / 2 right? Well, if the length is odd, since it is integer division we will get an integer which will work for the centre of the array. For this example, 5 / 2 = 2 with integer division which is the middlepoint of the array. However, if the array has even length then the median is the average of the middle two values. To get the average of two values we simply add them together and divide by 2. This all means our final get_median function is:

 fn get_median(input: [i32; 5]) -> i32 {
     let mut array = input;
     //sort array
     for index1 in 0..array.len() {
         for index2 in index1..array.len() {
             if array[index2] > array[index1] {
                 array.swap(index1, index2);
             }
         }
     }
     //return median
     if array.len() % 2 == 0 {
         //even
         let middle_value1 = array[array.len() / 2 - 1];
         let middle_value2 = array[array.len() / 2];
         let median = (middle_value1 + middle_value2) / 2;
         return median;
     } else {
         //odd
         let median = array[array.len() / 2];
         return median;
     }
 }

Note that we include brackets () around the middle values when adding them and dividing by 2. This is because maths in Rust follows the order of operations, so division occurs before addition. Since we want the addition to happen first, we put the brackets around to tell Rust it happens first. Also we use the value % 2 == 0 to check if the length is divisible by 2 which is what even or odd are. Next, if we plug this into our main:

 fn main() {
     let numbers = [9, 3, 8, 2, 7];
     let median = get_median(numbers);
     println!("{}", median);
 }

We get the 7 out that we calculated at the start!

However, what if we wanted to use different sized arrays? Note that we had to include the 5 in the type in the parameters in the get_median() function. Arrays are quite limited in that they must always have exactly the number of elements we say before we run the program. This also means the function we wrote can only take arrays of size 5. The solution to this problem is to use vectors instead. A vector is like an array in that it stores a bunch of the same type of thing in order, but the difference is they can grow and shrink and have different numbers of elements in them. The easiest way to create a vector is to use vec![] as follows:

 fn main() {
     let numbers = vec![9, 3, 8, 2, 7];
     let median = get_median(numbers);
     println!("{}", median);
 }

Note that we are using the ! at the end again, much like println! The ! shows that this is a macro. Macros are somewhat more complicated so will be explained later. For now, using vec![] like this will produce a vector that is the same as the array before. Then we change the get_median function to:

 fn get_median(input: Vec<i32>) -> i32 {
     //the inside of the function stays the same
 }

We just change the type of the input and it will work correctly. Note that we have to use <> after Vec to indicate the type. Also, we don't have to write the number of elements into the type since a vector can change its size. Now, we can change main() to try different sets of numbers:

 fn main() {
     let numbers1 = vec![9, 3, 8, 2, 7];
     println!("{}", get_median(numbers1));
     let numbers2 = vec![5, 3, 5, 7, 6, 9, 10, 9];
     println!("{}", get_median(numbers2));
     let numbers3 = vec![1, 40, 26, 3];
     println!("{}", get_median(numbers3));
 }

And after running this, we get out: 7, 6 and 14?

Wait that's not right, 7 is correct but the median of the second one should be 6.5 as the average between 6 and 7. And the last one should be 14.5 as the average between 3 and 26. This is because we're doing integer division here, so it gives us the integer value, which isn't what we want. This brings us on to the next topic: number conversions.

Next: Number Conversions