How to move items from one vector to another?

Basically, I want to implement a take_while, but by removing elements from a src vector and pushing them to a dst vector. I was able to solve this with this monstrosity:

use std::collections::VecDeque;

fn pop_push_while<T>(src: Vec<T>, dst: &mut Vec<T>, predicate: &dyn Fn(&T) -> bool) -> Vec<T> {
    let mut src_dq = VecDeque::from(src);
    while !src_dq.is_empty() {
        let item = src_dq.pop_front().unwrap();
        if !predicate(&item) {
            src_dq.push_front(item);
            break;
        }
        dst.push(item);
    }
    Vec::from(src_dq)
}

fn main() {
    let mut src:Vec<usize> = vec![0, 1, 2, 3, 4, 5];
    let mut dst:Vec<usize> = Vec::new();
    
    src = pop_push_while(src, &mut dst, &|i| i < &2);
    assert_eq!(src, vec![2, 3, 4, 5]);
    assert_eq!(dst, vec![0, 1]);
    
    src = pop_push_while(src, &mut dst, &|i| i < &5);
    assert_eq!(src, vec![5]);
    assert_eq!(dst, vec![0, 1, 2, 3, 4]);
}

It works, but I have a few issues with it:

  1. It seems I annotated the predicate wrong, because passing &|i| i < &2 looks weird. I never had to do this for builtin calls that take a predicate.
  2. I'm not happy with the fact that I can't modify src in place. My thinking here was that I should move src since after the call the original src is no longer valid. This happens because I internally create a VecDeque from it and return a new vector.
  3. In general my code looks rather complicated. Given the vast options in the standard library, my guess is that I'm just missing something.

Any recommendations?

Maybe have a look the nightly drain_filter, combined with take_while and dst.extend that could be good.

Most callbacks in the standard library are designed with generics, not dynamic dispatch, that's why you have to pass a reference.

1 Like

Oh dang, that is slick:

#![feature(drain_filter)]

fn main() {
    let mut src:Vec<usize> = vec![0, 1, 2, 3, 4, 5];
    let dst = src.drain_filter(|i| *i < 2).collect::<Vec<_>>();

    assert_eq!(src, vec![2, 3, 4, 5]);
    assert_eq!(dst, vec![0, 1]);
}
1 Like

Sidenote, since I generally don't care to overallocate, I would often use partition and get the 3 resulting Vec (the original and the two new ones).

Does this refer to my question about the annotation of predicate? If yes, how would the "correct" annotation look like to make this behave like all the other callbacks?

I'm on my phone right now, but it would be something like:

fn foo<T, P>(value: &T, predicate: F) -> bool
where
    P: Fn(&T) -> bool,
{
    predicate(value)
}

Obviously a pretty dumb example, but look at the signatures of the iterator trait's methods.

Here is my solution on stable:

fn drain_into_while<T, P>(src: &mut Vec<T>, dst: &mut Vec<T>, predicate: P)
where
    P: Fn(&T) -> bool,
{
    while !src.is_empty() {
        if predicate(&src[0]) {
            dst.push(src.remove(0));
        } else {
            break;
        }
    }
}

fn main() {
    let mut src:Vec<usize> = vec![0, 1, 2, 3, 4, 5];
    let mut dst:Vec<usize> = Vec::new();
    
    drain_into_while(&mut src, &mut dst, |i| *i < 2);

    assert_eq!(src, vec![2, 3, 4, 5]);
    assert_eq!(dst, vec![0, 1]);
    
    drain_into_while(&mut src, &mut dst, |i| *i < 5);
    assert_eq!(src, vec![5]);
    assert_eq!(dst, vec![0, 1, 2, 3, 4]);
}

Thanks a ton @erelde for the pointers!

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.