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?
1 Like
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).
1 Like
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.