user1381004
user1381004

Reputation:

Filter Vector of Generic Structs

I'm trying to get this filter to work, my other tests pass but the find_by function isn't compiling. I'm getting an error no field name on type T. What am I missing here in regards to the filter accessing the fields? I made a version of this code without generics and it works correctly.

Here is my code:

pub struct Repository<T> {
    store: Vec<T>
}

impl<T> Repository<T> {
    pub fn create() -> Repository<T> {
        Repository::<T> {
            store: vec![]
        }
    }

    pub fn find_all(self) -> Vec<T> {
        self.store
    }

    pub fn add(&mut self, item: T) {
        &mut self.store.push(item);
    }

    // this method returns an error
    pub fn find_by(self, name: &str) -> Vec<T> {
        self.store.into_iter().filter(|&e| e.name == name).collect()
    }

}

#[cfg(test)]
mod tests {
    use super::*;

    // ...

    #[test]
    fn can_find_objects_in_repository_by_param() {
        #[derive(Debug, PartialEq)]
        pub struct Cat { pub name: String };
        impl Cat {
            pub fn create(name: &str) -> Cat { Cat { name: name.to_string() } }
        }
        
        let mut repo = Repository::<Cat>::create();

        let c1 = Cat::create("Mittens");
        let c2 = Cat::create("Tiger");

        repo.add(c1);
        repo.add(c2);

        assert_eq!(repo.find_by("Tiger"), vec![Cat { name: "Tiger".to_string() }]);
    }
}

If I remove the filter the code compiles and the test fails with the following error as expected:

left: `[Cat { name: "Mittens" }, Cat { name: "Tiger" }]`,
right: `[Cat { name: "Tiger" }]`'

Upvotes: 0

Views: 527

Answers (1)

Cl&#233;ment Joly
Cl&#233;ment Joly

Reputation: 983

It works if you define a trait Named your Cat will implement. This trait contains the name() method, thus solving your problem.

Here is the main changes to apply:

// …
pub trait Named {
    fn name(&self) -> &str;
}

impl<T> Repository<T> where T: Named {
// …
    // this method returns an error
    pub fn find_by(self, name: &str) -> Vec<T> {
        self.store.into_iter().filter(|e| e.name() == name).collect()
    }

// …
#[cfg(test)]
mod tests {
        // …
        impl Named for Cat {
            fn name(&self) -> &str {
                &self.name
            }
        }

See the whole code on rust playground.

Upvotes: 1

Related Questions