I have a collection(for time being Vec
) which I want to clone but having only the elements that specify a criteria. Basically I want the C++ functionality of std::copy_if. I want the below generic code to work for any collection type. If the input collection type is a Vec
, I want the output collection type to be also a Vec
.
fn clone_if<'a, List, ItemType, BinaryPredicate>(list: List, binary_predicate: BinaryPredicate) -> List
where
List: Clone + IntoIterator<Item = &'a ItemType> + Copy,
ItemType: 'a,
BinaryPredicate: Fn(&ItemType) -> bool
{
let mut res = list.clone();
for item in res.into_iter() {
if !binary_predicate(&item) {
// res.remove();
}
}
res
}
fn main() {
let first_list = vec![1, 2, 3, 4, 5, 6];
let second_list = clone_if(&first_list, |item| {
(item & 1) == 0
});
assert_eq!(vec![2, 4, 6], *second_list);
}
The current function signature of clone_if
is not compatible with what you want to achieve. So I rewrote it slightly to probably match what you actually want.
Here you go:
fn clone_if<List, ItemType, BinaryPredicate>(list: &List, binary_predicate: BinaryPredicate) -> List
where
for<'a> &'a List: IntoIterator<Item = &'a ItemType>,
List: FromIterator<ItemType>,
ItemType: Clone,
BinaryPredicate: Fn(&ItemType) -> bool,
{
list.into_iter()
.filter(|val| binary_predicate(*val))
.cloned()
.collect()
}
fn main() {
let first_list = vec![1, 2, 3, 4, 5, 6];
let second_list = clone_if(&first_list, |item| (item & 1) == 0);
assert_eq!(vec![2, 4, 6], *second_list);
}
Explanation:
list: &List
- to call it the way you do in main
, you need a reference here.for<'a> &'a List: IntoIterator<Item = &'a ItemType>
- you want the list
reference to produce references to its elements when iterated over, which is what &Vec
is compatible with. That prevents unnecessary copying.List: FromIterator<ItemType>
- Needed to produce the output value with collect()
.ItemType: Clone
- you want to create clones of the input items in your output list.Clone
and Copy
s were removed because they were unnecessary.Then the algorithm itself:
.into_iter()
- Create an Iterator<Item = &ItemType>
from your input list.filter(|val| binary_predicate(*val))
- Do the filtering. No copies are made yet. The reason why a closure and dereference is needed is because filter
takes references to the items iterated over, which in this case will be &&ItemType
. So a small wrapper is needed to convert &&ItemType
to &ItemType
, which is what binary_predicate
needs..cloned()
- Clone all the items iterated over. This will convert the Iterator<Item = &ItemType>
to an Iterator<Item = ItemType>
. Note that this is done after filter()
to prevent copying items that get filtered out anyway..collect()
use FromIterator
to convert the Iterator<Item = ItemType>
to List
.