pmeier
November 17, 2022, 10:27pm
1
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:
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.
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.
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?
erelde
November 17, 2022, 10:37pm
2
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
pmeier
November 17, 2022, 10:43pm
3
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
erelde
November 17, 2022, 10:46pm
4
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).
pmeier
November 17, 2022, 10:48pm
5
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?
erelde
November 17, 2022, 10:51pm
6
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.
pmeier
November 17, 2022, 11:17pm
7
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
system
Closed
February 15, 2023, 11:17pm
8
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.