3
\$\begingroup\$

I am reading the Rust book. I am on chapter 8; this is the assignment:

Given a list of integers, use a vector and return the median (when sorted, the value in the middle position) and mode (the value that occurs most often; a hash map will be helpful here) of the list.

And here is my code for it:

  `
     #![allow(unused)]

  use std::collections::HashMap;
fn main() {
    let mut integers = vec![
        4, 9, 6, 7, 3, 4, 5, 6, 2, 13, 9, 5, 2, 3, 6, 8, 4, 5, 5, 6, 9, 8, 6,
    ];
    // arrange numbers in ascending order
    integers.sort_by(|a, b| a.cmp(b));
    let int_length = integers.len();
    // check if the length of the numbers it is odd or even
    if let 0 = int_length % 2 {
        // for even
        let mut answer = 0;
        answer = int_length / 2;
        let median = (integers[answer] + integers[answer + 1]) / 2;
        println!("the median is {}", median)
    } else {
        //for odd
        let mut answer = 0;
        answer = int_length / 2 + 0.5 as usize;
        let median = integers[answer];
        println!("the median is {}", median)
    }
    let mut values_in_hashmap = HashMap::new();
    // record the value of each occurence of the numbers in hashmap
    for values in integers {
        let count = values_in_hashmap.entry(values).or_insert(0);
        *count += 1;
    }

    let mut current_highest_value = 0;
    let mut modes = vec![];
    // loop through the hashmap to get the highest value in the hashmap
    for (key, value) in &values_in_hashmap {
        if value > &current_highest_value {
            current_highest_value = *value;
        }
    }
    // push all the number with same value as the highest value to an array
    for (key, value) in &values_in_hashmap {
        if *value == current_highest_value {
            modes.push(key)
        }
    }
    println!(" the modes {:?}", modes);
}


  `
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

and welcome to code review. I should be clear that I am by no means an experienced rust programmer. It seems there's not many of those here so I'll give it a go.

The first thing that jumps out at me is that you're embedding the vector to explore directly into main, rather than having it as a parameter to a function as in chapter 3. Personally I would actually have three functions: main (which you have), and separate functions for median and mode. So main would be limited to defining the data to process, calling the median and mode functions, and displaying the results.

The second thing which I notice is in you're consistently using let mut. That is legal, but it loses one of the biggest advantages of programming in Rust. The Rust compiler is very well set up to help understand your program by restricting what can change where, and the best way to lean into that advantage is to ensure that things don't change unless they really need to. For example, you have let mut answer = 0; followed immediately by answer = int_length / 2;. Instead, you could just have let answer = int_length / 2; which immediately sets the right value for that variable and keeps hold of it.

A particularly prominent example of this is let mut integers. If you had, for example, a separate median function which accepted the vector as a parameter, (fn median(integers: &vec) {) then the compiler would not allow you to edit (e.g. sort) that vector. To be clear, you could add a mut to the reference type there too, but you shouldn't. It would be a very surprising median function if it also rearranged its input! Instead by deliberately not marking the parameter as mut you would allow other code to call your function without fear, and your function would be responsible for allocating appropriate working copies.

A range of smaller observations

  • If you're not looking to assign something on success, just use if instead of if let.
  • It is worth thinking about the meaning of some of your variable names. For example, "integers" and "answer" are not very descriptive for the purposes you've put them to.
  • int_length / 2 + 0.5 is not legal because the types don't match. int_length / 2 is an integer, and 0.5 is a float. (Yes, I know that mathematically an odd number divided by two is not an integer. Rust does integer division between two integers, so the extra 0.5 gets dropped). One thing you could do instead is use (int_length + 1) / 2, so everything will be an integer type.
  • Finally, make sure you run cargo check on code to make sure it'll compile. That's the other half of rust being really good at warning you if there's a problem.
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.