Part 3. Reversing a vector (15 points)

In the rest of the lab, you’re going to write some short functions dealing with vectors and strings and you’re going to write some unit tests for the functions.

Continue working in the main.rs file of your iteration project.

Your task

Implement the function reversed_vec.

fn reversed_vec(input_data: &[i32]) -> Vec<i32> {
    let mut result: Vec<i32> = Vec::new();
    todo!("Finish this function")
}

Two things to notice

  1. The input argument input_data is of type &[i32]. We’ll talk more about this type later, but for now, think of this as a reference to a Vec. We saw this above when we wrote for x in &data {} where data was a Vec so &data essentially gives us a &[i32].
  2. The result variable is declared mutable (with the mut keyword) so we can modify it by inserting elements using the push() function.

Before you remove the todo!() and implement this function, let’s write a unit test similar to the unit tests you wrote in CSCI 151.

At the bottom of main.rs, add the following code.

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn reversed_vec_empty() {
        let data = vec![];
        assert_eq!(reversed_vec(&data), vec![]);
    }
}

There’s a lot to unpack here. The first line, #[cfg(test)] tells the compiler that the item that follows it should only be compiled when we’re compiling tests. And that item is a module named test. (The name is arbitrary but is traditionally called test.) We’ll talk more about modules later.

The use super::*; line makes all of the functions in main.rs outside the test module available for use inside the test module.

Finally, each unit test we write is just a Rust function that is annotated with #[test]. If you omit this annotation, the function will not be treated as a test.

Run the command $ cargo test. You should see some warnings followed by a test failure (remember, we didn’t implement reversed_vec() so it makes sense our test should fail).

running 1 test
test test::reversed_vec_empty ... FAILED

failures:

---- test::reversed_vec_empty stdout ----
thread 'test::reversed_vec_empty' panicked at 'not yet implemented: Finish this function', src/main.rs:19:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    test::reversed_vec_empty

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Notice that the output tells us our code panicked (i.e., failed) on line 19 of main.rs.

Before implementing reversed_vec(), write two more tests

    #[test]
    fn reversed_vec_one() {
        todo!();
    }

    #[test]
    fn reversed_vec_three() {
        todo!();
    }

that test the result of reversing a vector with 1 element and a vector with 3 elements.

$ cargo test should now show three failing tests.

Implement the rest of reversed_vec().

Hint

  1. You can create an iterator over input_data using input_data.iter(). Some iterators let you create reverse iterators using the rev() function. In other words, given one iterator it, you can create a new iterator via it.rev() that will iterate in reverse order. So for x in input_data.iter().rev() { } will iterate over input_data in reverse order.
  2. The iterator returned by input_data.iter() will not make copies of the elements in input_data. Instead, the value returned by the iterator will be a reference to the elements. That is, in
    for x in input_data.iter() { }
    the type of x is &i32 and not i32. Since our result vector holds i32 and not &i32, we need to dereference the reference x to get the underlying integer. We use the * operator to do this. For example,
    for x in input_data.iter() {
        // result.push(x); // Fails because x has type &i32
        result.push(*x); // Succeeds because *x has type i32
    }
    shows the incorrect and correct ways to insert the element into result.
Once you implement your function, run `$ cargo test` and make sure you have 3 passing tests and no failing tests.

Tip

You’re probably getting a warning at this point about reversed_vec not being used. Add the line

#![allow(dead_code)]

to the very top of main.rs and it’ll stop warning about the functions that we’re only using in tests.