Trait implementation for `Vec<&MyStruct>` and `Vec<MyStruct>`

I have a trait MyTrait which I can implement for Vec<&MyStruct>. (it only needs reading access, hence the use of references) However, in my code I have a Vec<MyStruct>. Now I want to use the same implementation. However, it doesn't work. The compiler says it is not implemented for Vec<MyStruct>. How to fix this?

Knowing what the trait actually does would be helpful, but my guess is that you should implement it for &[MyStruct].

2 Likes

Here is my actual code:

struct Filter {
    regex: Regex,
    want: bool,
    description: String,
}

impl Filter {
    #[inline]
    fn is_ok(&self, hay: &str) -> bool {
        self.regex.is_match(hay) == self.want
    }
}

trait FilterList {
    fn is_ok(&self, hay: &str) -> bool;
}

impl FilterList for Vec<&Filter> {
    #[inline]
    fn is_ok(&self, hay: &str) -> bool {
        self.iter().all(|f| f.is_ok(hay))
    }
}

A Filter is something that takes in a string (called "hay") and then is either ok or not ok. A FilterList is ok if all of its filters are ok.

I think I should probably implement it for Iterator<Item = &Filter>, right? I currently figure out how to do this. (it says i need to put dyn somewhere?! why? and then it says I can't invoke all(), for whatever reason..) Any help is appreciated..

First, change your trait to take self by value. You can still implement it for references when needed, but Iterator requires &mut self or self, so &self would make that impossible. Then you can implement it with a generic.

pub trait FilterList {
    fn is_ok(self, hay: &str) -> bool;
}

impl<'a, I> FilterList for I
where
    I: Iterator<Item = &'a Filter>,
{
    fn is_ok(mut self, hay: &str) -> bool {
        self.all(|f| f.is_ok(hay))
    }
}

If you want this to work on Vec, you can instead implement it for IntoIterator. This will also work for all Iterator types, since they automatically implement IntoIterator.

impl<'a, I> FilterList for I
where
    I: IntoIterator<Item = &'a Filter>,
{
    fn is_ok(self, hay: &str) -> bool {
        self.into_iter().all(|f| f.is_ok(hay))
    }
}

Implementations like the above in the form impl<T> Trait for T are blanket impls. You can only make one per trait.

When using this, remember that &Vec<Filter> implements IntoIterator<Item = &Filter>, so you can do (&filter_vec).is_ok(hay), which is the same as filter_vec.iter().is_ok(hay).

1 Like

I absolutely don't get why I need mut self and self instead of &self and IntoIterator instead of Iterator (all of those go in the direction of mutability, though all I do only needs reading access?!), but it works. Thank you very very much!

Obtaining items from an Iterator mutates the Iterator You can do this with an owned self or borrowed &mut self, but any &mut Iterator also implements Iterator, so making it use an owned self means you can use owned or borrowed iterators.

Calling IntoIterator::into_iter requires ownership.

mut x and x are the same function signature. The former is just shorthand for:

fn f(x: T) {
    let mut x = x;
}
1 Like

ok but why can't stay my implementation of Filter::is_ok(...) with &self?

You could, but there is no point*. &self in a trait is nearly always more restrictive than self. You can implement a trait that takes self for references, but you can't implement a trait that takes &self for owned types. These two trait impls are very similar:

pub trait A {
    fn a(self);
}

impl<'a> A for &'a String {
    fn a(self) {
        println!("{}", &self);
    }
}

pub trait B {
    fn b(&self);
}

impl B for String {
    fn b(&self) {
        println!("{}", self);
    }
}

Both A::a and B::b take &String. But the trait A is more flexible.

If you use &self, you can still sort of implement it for Iterator.

impl<'b, I> FilterList for I
where
    for<'a> &'a I: Iterator<Item = &'b Filter>,
{
    fn is_ok(&self, hay: &str) -> bool {
        let mut this = self;
        this.all(|f| f.is_ok(hay))
    }
}

This compiles, but it is extremely rare (maybe impossible?) that a type will fulfill the requirements.

It is also possible to implement it for IntoIterator, and this one is more useful.

impl<I> FilterList for I
where
    for<'a> &'a I: IntoIterator<Item = &'a Filter>,
{
    fn is_ok(&self, hay: &str) -> bool {
        self.into_iter().all(|f| f.is_ok(hay))
    }
}

There's many implementations of IntoIterator for references, like slices &[T]. However, this is still more restrictive than taking self by value.


* Traits that are used as trait objects need to take a reference to self, but &self has the issues above, and &mut self still has the issue with IntoIterator.

1 Like

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.