Using trait bounds (IntoIter) in generic method

I am trying to implement a generic method that takes something iterable as argument.

In the example below, this works for the contains method of Cluster (corresponding test1).

However, I fail to implement a method in ClusterListthat applies the same contains method to a vector of these Clusters.

The error is:

             if cluster.contains(elements) {  //  <== does not work
   |                             ^^^^^^^^ value moved here, in previous iteration of loop
   |

I understand that indeed here I move elements, which I can do only once.

However, I also cannot pass a reference &elements, which is the part I don't understand:

Then I get:

   Compiling iter_test v0.1.0 (/data/_programming/assign_topics/iter_test)
error[E0277]: `&I` is not an iterator
  --> src/main.rs:61:33
   |
61 |             if cluster.contains(&elements) {  //  <== does not work
   |                        -------- ^^^^^^^^^ `&I` is not an iterator
   |                        |
   |                        required by a bound introduced by this call
   |
   = help: the trait `Iterator` is not implemented for `&I`
   = note: required for `&I` to implement `IntoIterator`
note: required by a bound in `Cluster::contains`
  --> src/main.rs:20:14
   |
19 |     pub fn contains<'a, I>(&self, elements: I) -> bool
   |            -------- required by a bound in this associated function
20 |     where I: IntoIterator<Item = &'a usize> {
   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Cluster::contains`
help: consider dereferencing here
   |
61 |             if cluster.contains(*&elements) {  //  <== does not work
   |                                 +

I understand that I is not an iterator, but I thought that it would have the trait IntoIterator.

It seems that I fundamentally misunderstood something here and would be happy to get some help !

Here is the complete example:

#[derive(Debug, Clone)]
pub struct Cluster {
    elements: Vec<usize>,
}

impl Cluster {
    pub fn new() -> Self {
        Self { elements: vec![] }
    }

    pub fn add(&mut self, element: usize) {
        self.elements.push(element);
    }

    pub fn contains<'a, I>(&self, elements: I) -> bool
    where I: IntoIterator<Item = &'a usize> {
        for element in elements {
            if !self.elements.contains(element) {
                return false;
            }
        }
        true
    }
}

impl From<Vec<usize>> for Cluster {
    fn from(item: Vec<usize>) -> Self {
        Self { elements: item }
    }
}

impl From<&Vec<usize>> for Cluster {
    fn from(item: &Vec<usize>) -> Self {
        Self { elements: item.clone() }
    }
}

// -------------------------- cluster list ---------------------------

#[derive(Debug, Clone)]
struct ClusterList {
    clusters: Vec<Cluster>,
}

impl ClusterList {
    pub fn new() -> Self {
        Self { clusters: vec![] }
    }

    pub fn add(&mut self, cluster: Cluster) {
        self.clusters.push(cluster);
    }

    pub fn contains<'a, I>(&self, elements: I) -> bool
    where I: IntoIterator<Item = &'a usize> {
        for cluster in self.clusters.iter() {
            // if cluster.contains(elements) {  //  <== does not work
            //     return true;
            // }
        }
        false
    }
}

// ------------------------------ tests ------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use std::assert_eq;
    use std::collections::BTreeSet;

    #[test]
    fn test1() {
        let c : Cluster = vec![0, 1, 2, 3].into();

        let set1 : Vec<usize> = vec![1,2,3];
        let set2 = BTreeSet::<usize>::from([1,2,3,4]);

        assert_eq!(c.contains(&set1), true);
        assert_eq!(c.contains(&set1), true);
        assert_eq!(c.contains(&set2), false);
    }

    #[test]
    fn test2() {
        let c : Cluster = vec![0, 1, 2, 3].into();
        let mut clist = ClusterList::new();
        clist.add(c);

        let set1 : Vec<usize> = vec![1,2,3];
        let set2 : Vec<usize> = vec![1,2,3,4];
        assert_eq!(clist.contains(&set1), true);
        assert_eq!(clist.contains(&set2), false);
    }
}

fn main() {
}

You could do something like

impl ClusterList {
    pub fn contains<'a, I>(&self, elements: &'a I) -> bool
    where
        &'a I: IntoIterator<Item = &'a usize>,

You could also generalize to

impl ClusterList {
    pub fn contains<'a, I>(&self, elements: I) -> bool
    where
        I: IntoIterator<Item = &'a usize> + Copy,

Of course, opportunity for further generalization alwas exists, too ^^

1 Like

Just because I: SomeTrait doesn't mean &I: SomeTrait. References to I are their own types, distinct from &I. In fact, it is typical for containers and references to containers to both implement IntoIterator to do different things -- return the owned contents of the container, or return borrowed references to the contents of the container.

Maybe you understood that, but expected that "if I: IntoIterator, &I: IntoIterator must also be true". There is no such blanket implementation or other rule that this has to be the case. So if you need &I: IntoIterator<Item = &'a usize> to be a property you can use, it has to be part of the trait bounds.

But what you actually want is probably one of the changes @steffahn suggested.

Incidentally, their second "generalize to" suggestion is also what the compiler error suggested.

15 |     pub fn contains<'a, I>(&self, elements: I) -> bool
   |            -------- in this method          ^ this parameter takes ownership of the value
help: consider further restricting this bound
   |
55 |     where I: IntoIterator<Item = &'a usize> + Copy {
   |                                             ++++++
2 Likes

Indeed, that is what I expected... Thanks for the clarification!

and thanks for the example with the borrow trait for Item, this is another part I could not solve.

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.