Abstract contains method

⚓ Rust    📅 2025-05-16    👤 surdeus    👁️ 5      

surdeus

Warning

This post was published 96 days ago. The information described in this article may have changed.

Info

This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Abstract contains method

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"]);
}

playground

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

Read full topic

🏷️ rust_feed