0

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" }]`'
Pyx
  • 340
  • 2
  • 12
  • You are trying to access a field `name` on `T`, but that doesn't exist. – starblue Feb 02 '19 at 08:28
  • `Repository` has to be valid for _any_ possible `T`, so you cannot assume it has a `name` property. Constrain `T` with a trait that has an accessor for `name` or else use a closure instead of a string for the filter argument. – Peter Hall Feb 02 '19 at 09:13
  • Possible duplicate of [Is it possible to access struct fields from within a trait?](https://stackoverflow.com/questions/28219730/is-it-possible-to-access-struct-fields-from-within-a-trait) – Peter Hall Feb 02 '19 at 09:22

1 Answers1

1

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.

Clément Joly
  • 858
  • 9
  • 27