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);
}
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);
}
Share
Improve this question
edited Feb 2 at 8:34
cafce25
27.6k5 gold badges45 silver badges58 bronze badges
asked Feb 2 at 7:01
HarryHarry
3,1421 gold badge24 silver badges46 bronze badges
4
|
1 Answer
Reset to default 1The 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 inmain
, you need a reference here.for<'a> &'a List: IntoIterator<Item = &'a ItemType>
- you want thelist
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 withcollect()
.ItemType: Clone
- you want to create clones of the input items in your output list.- All other
Clone
andCopy
s were removed because they were unnecessary.
Then the algorithm itself:
.into_iter()
- Create anIterator<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 becausefilter
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 whatbinary_predicate
needs..cloned()
- Clone all the items iterated over. This will convert theIterator<Item = &ItemType>
to anIterator<Item = ItemType>
. Note that this is done afterfilter()
to prevent copying items that get filtered out anyway..collect()
useFromIterator
to convert theIterator<Item = ItemType>
toList
.
list.iter().filter (predicate).cloned().collect()
– Jmb Commented Feb 2 at 7:19clone_if
? – Harry Commented Feb 2 at 8:17clone_if
function is flawed as it is right now. You cannot return a reference to an item created inside of a function. You need to have ownership. So your return type needs to be some variant ofVec<T>
, while your argument probably wants to be&Vec<T>
, which is impossible to achieve with your current function signature. I'll propose a better signature. – Finomnis Commented Feb 2 at 8:23