Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return last n elements of vector in Rust without mutating the vector

Tags:

vector

rust

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);
    }
}
like image 841
stevensonmt Avatar asked Jun 14 '17 16:06

stevensonmt


3 Answers

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.

like image 88
Shepmaster Avatar answered Oct 29 '22 00:10

Shepmaster


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);
like image 4
Boiethios Avatar answered Oct 29 '22 02:10

Boiethios


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]
like image 4
Green Bird Avatar answered Oct 29 '22 02:10

Green Bird