Warning
This post was published 96 days ago. The information described in this article may have changed.
Dear reader,
I was working on some code where I wanted to generalize over set membership through a sequential scan and hashtable lookup. I tried writing a trait with a contains
method but got in trouble when trying to actually use it.
trait CollectionContains<T> {
fn contains(&self, item: &T) -> bool;
}
impl<T> CollectionContains<T> for Vec<T>
where
T: PartialEq,
{
fn contains(&self, item: &T) -> bool {
<[T]>::contains(self, item)
}
}
fn plain<'a>(x: &mut Vec<String>, keep: Vec<&'a str>) {
x.retain(|item| keep.contains(&item.as_str()));
}
fn generic<'a>(x: &mut Vec<String>, keep: impl CollectionContains<&'a str>) {
// -- lifetime `'a` defined here
x.retain(|item| keep.contains(&item.as_str()));
// ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// | |
// | `item` escapes the closure body here
// | argument requires that `'1` must outlive `'a`
// `item` is a reference that is only valid in the closure body
// has type `&'1 String`
}
fn main() {
plain(&mut vec!["a".to_string(), "b".to_string()], vec!["a"]);
generic(&mut vec!["a".to_string(), "b".to_string()], vec!["a"]);
}
The plain
function is just there to show the similarity. I don't understand what is different between the concrete type in plain
and the bounds I placed on generic
, and if the bounds can be adjusted to match what happens for the concrete type. I tried changing the signature to:
fn generic(x: &mut Vec<String>, keep: impl for<'a> CollectionContains<&'a str>) {
But that leads to the following at the call-site:
error: implementation of `CollectionContains` is not general enough
--> openapi-processor/src/bin/help.rs:31:5
|
31 | generic(&mut vec!["a".to_string(), "b".to_string()], vec!["a"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `CollectionContains` is not general enough
|
= note: `Vec<&'2 str>` must implement `CollectionContains<&'1 str>`, for any lifetime `'1`...
= note: ...but it actually implements `CollectionContains<&'2 str>`, for some specific lifetime `'2`
Rather than defining this trait, I could pass in a contains_fn
closure. That might be a superior solution anyway, but I can't help but wonder why this approach is hard and how to solve it.
5 posts - 3 participants
🏷️ rust_feed