Valentin Lorentz
Valentin Lorentz

Reputation: 9753

Alternative to Vec::chunks() that consumes the vector

I have the following function, which takes a vector as argument and returns a vector of its pairs of elements:

fn to_pairs(flat: Vec<u64>) -> Vec<(u64, u64)> {
    assert!(flat.len() % 2 == 0);
    let mut pairs = Vec::new();
    pairs.reserve(flat.len() / 2);
    for pair in flat.chunks(2) {
        assert!(pair.len() == 2);
        pairs.push((pair.get(0).unwrap().clone(), pair.get(1).unwrap().clone()));
    }
    pairs
}

I want consume the vector flat so I don't have to clone its elements when constructing the pair. Is it possible to do so without reimplementing a variation of Vec::chunks() myself?

Upvotes: 2

Views: 186

Answers (1)

Shepmaster
Shepmaster

Reputation: 430673

I want consume the vector flat so I don't have to clone its elements when constructing the pair.

Convert the input Vec into an iterator, then take two things from the iterator at a time. Essentially, you want the same thing as processing a Range (an iterator) in chunks:

fn to_pairs<T>(flat: Vec<T>) -> Vec<(T, T)> {
    let len = flat.len();

    assert!(len % 2 == 0);
    let mut pairs = Vec::with_capacity(len / 2);

    let mut input = flat.into_iter().peekable();

    while input.peek().is_some() {
        match (input.next(), input.next()) {
            (Some(a), Some(b)) => pairs.push((a, b)),
            _ => unreachable!("Cannot have an odd number of values"),
        }
    }

    pairs
}

fn main() {
    assert_eq!(vec![(1,2), (3,4)], to_pairs(vec![1,2,3,4]));
    assert_eq!(vec![(true,true), (false,false)], to_pairs(vec![true,true,false,false]));
}

The assert!(len % 2 == 0); is quite important here, as Iterator makes no guarantees about what happens after the first time next returns None. Since we call next twice without checking the first value, we could be triggering that case. In other cases, you'd want to use fuse.

As pointed out by Kha, you could simplify the while loop a bit:

let mut input = flat.into_iter();

while let (Some(a), Some(b)) = (input.next(), input.next()) {
    pairs.push((a, b));
}

Upvotes: 1

Related Questions