23

I am struggling to find a way to take two values from the end of a vector, sum those values, and push the sum to the vector.

I have found that pop, truncate, and drain do not work because they remove the values from the original vector.

fn main() {
    println!("Which Fibonacci number would you like to find?");

    let mut fib_num = String::new();

    io::stdin().read_line(&mut fib_num)
        .expect("Failed to read line");

    let fib_num: u32 = fib_num.trim().parse()
        .expect("Please enter a number");

    let mut stored_nums: Vec<u32> = vec![0, 1];

    while fib_num > stored_nums.len() as u32 {
        let mut limit = stored_nums.len();
        let mut new_num1 = stored_nums.pop().unwrap();
        let mut new_num2 = stored_nums.pop().unwrap_or(0);
        stored_nums.push(new_num1 + new_num2);
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
stevensonmt
  • 702
  • 1
  • 6
  • 19

3 Answers3

28

You need to consider the case where the vector doesn't have two items.

I'd use iterator adapters like Iterator::rev and Iterator::take and then finish with Iterator::sum:

let sum = stored_nums.iter().rev().take(2).sum();
stored_nums.push(sum);

This allows you to avoid explicit handling of cases where the vector / slice / iterator is too short but the code still deals with it implicitly.


You could also directly index into the slice:

let len = stored_nums.len();
let sum = stored_nums[len - 1] + stored_nums[len - 2];
stored_nums.push(sum);

This will panic if there are less than 2 elements, however.

You could attempt to deal with the vector being too short in this case, but it's a bit verbose:

fn add_last_two(nums: &[u32]) -> Option<u32> {
    let len = nums.len();

    let idx_a = len.checked_sub(1)?;
    let idx_b = len.checked_sub(2)?;

    let a = nums.get(idx_a)?;
    let b = nums.get(idx_b)?;

    Some(a + b)
}

fn main() {
    let mut stored_nums: Vec<u32> = vec![0, 1];
    let sum = add_last_two(&stored_nums).unwrap_or(0);
    stored_nums.push(sum);
}

Note that it might be nicer to use a Fibonacci iterator and just collect that into a Vec.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    Iterator is the way to go. Thanks. Because I'm prepopulating the vec with two items I didn't think to worry about the case where there might not be two items. I also changed my integer type to u64 as u32 topped out at <50th number in the sequence. – stevensonmt Jun 14 '17 at 18:05
8

To retrieve the last n elements, you could just convert it to a slice.

E.g. Get last 3 elements:

let v = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let last3 = v.as_slice()[v.len()-3..].to_vec();

println!("{:?}", last3); // [7, 8, 9]
Green Bird
  • 179
  • 2
  • 6
4

You can use a reverse iterator on the Vec:

let (n1, n2) = {
    let mut rev_iter = stored_nums.iter().rev();
    (rev_iter.next().unwrap().clone(), rev_iter.next().unwrap().clone())
};
stored_nums.push(n1 + n2);
Boiethios
  • 38,438
  • 19
  • 134
  • 183
  • The behavior of calling `next` after an iterator returns `None` the first time is undefined. This answer calls `next` multiple times *without* checking if each preceding one is `None`, thus it can lead to unexpected results. – Shepmaster Jun 14 '17 at 17:19
  • @Shepmaster I know. The length of the vector does not go under 2, and I call `next` 2 times only, so there is no problem. – Boiethios Jun 14 '17 at 17:29
  • The *example* provided never goes below 2 elements, but the *question* is more general than the example. – Shepmaster Jun 14 '17 at 17:35
  • My answer is very contextual and did not aimed to give a general answer. Anyway, your answer is better as usual – Boiethios Jun 14 '17 at 17:49