Implementing fn for a struct that's stored in vector


#1

I want to use something like this

trait Clean {
    fn cleanup(&self) -> Self;
}
impl<Row> Clean for Vec<Row> {
    fn cleanup(&self) -> Self {
        for row in self.iter_mut()  {
            row.cleanup()
        }
        *self
    }
}
#[derive(RustcDecodable)]
struct Row {
    keyword: String,
    group: String,
}

impl Row {
    pub fn cleanup(&mut self) {
        Row::cleanup_field(&mut self.keyword);
        Row::cleanup_field(&mut self.group);
    }

    fn cleanup_field(string: &mut String) {
        // implementation not important
    }
}

So I can call it like this:

fn csv_to_vec(path: &str) -> Vec<Row> { .... }

let mut rows = &mut csv_to_vec(path).cleanup();

But this row.cleanup() gives me an error no method named 'cleanup' found for type '&mut Row' in the current scope and I can’t find any example that could help me with that. When I try to do impl &mut Row {} it starts to ask me for lifetimes, and all I can really do at this time is ask for help.

I was able to write this stuff without implementation for any structs or vectors in what is say to myself “functional way”, but wanted to try with impl to make it a little bit cleaner, and hide implementation details.


#2

Is no method named 'cleanup' found the first error you get? Because I’d first expect a problem with the fact that you’re using cleanup(&self) – immutable &self – and trying to call iter_mut().

Can you share a complete example on the playground?


#3

Pretty sure you don’t want this to be a generic over a generic variable Row, considering you have a struct called Row. Replace impl<Row> with simply impl to get rid of dynamic dispatch over ALL vectors.


#4

@xfix: That was it, I get other errors now, but probably I can handle them when I get to work with it later today or tomorrow :smiley: Thank you very much!

@cuviper: Yes, that was the only error I got, but after doing what @xfix suggested, I got “your” error, but I handled it, now I get other error, but it’s wording is perfectly clear so far :wink: But don’t have to handle it exactly now. I’ll share on playground complete working example later today/tomorrow. Thank you!


#5

My code got transformed a lot over those last 3 days a lot and I learned a ton.
But maybe someone will have a problem like me, so here it is, a working playground example https://is.gd/PsGkyW

Once again thank you for pushing me into the right direction.


#6

This is more of a stylistic thing, but in general Rust prefers to keep as much stuff immutable as possible. That way you don’t get errors where you called a function and it breaks something somewhere else. Most of the time it’s just as fast and tends to lead to much more readable code anyway.

In your example you mutate the original rows variable then make a copy of it to give back to the user. So after you call rows.cleanup() you get a new cleaned version, but it also accidentally corrupted the original by changing its fields. This probably wasn’t the desired effect and could lead to “interesting” bugs later on in a larger program.

Here’s a playground link where I’ve added some print statements to show what I mean: https://is.gd/g1lVUn

Here’s how I’d probably do it: https://is.gd/EsW52T

In particular notice that rows is the same before and after being cleaned and you now only need an immutable borrow to get a cleaned version.

Also, it looks like you forgot to explicitly implement the Clean trait for your Row. A bare impl Row {...} block is just adding a normal method onto the Row struct.


#7

Actually i just made vector of those rows, and immediately used cleanup() on it. But who knows what would I have to change in future, so all I can do is thank you for showing me that it’s best to make data immutable :slight_smile: Incidentally it fixed a very minor bug I had somewhere else, that I was sure was unrelated, but on the other hand I made more that immutable so I’m not sure of anything.