Check if retain from vector was successful?

Hello

I have a vector of Cat objects. Now I want to delete a cat with a specific name from the vector. I will be using the retain function for that.
But I also want to check if the retain function has deleted an object or not. How do I do that? It doesn't return anything.

One possible solution would be taking the difference of the size of the vector before and after the deletion of the object, but maybe there is a more clean solution?

Demo code:

//Struct Cat
pub struct Cat {
    pub name: String,
    age: u8,
    //color: CatColor,
    //race: CatRace,
}

fn main() {
    //Making vector to hold cats
    let mut cats = std::vec::Vec::<Cat>::new();
    
    //Initializing two Cat-objects
    let c1 = Cat {
        name: "Tom".to_string(),
        age: 16,
    };
    let c2 = Cat {
        name: "Mr. Miauw".to_string(),
        age: 6,
    };

    //Adding cats to vector
    cats.push(c1);
    cats.push(c2);
    
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //Here I want to delete the cats and check
    //if a cat was succesfully deleted!!!! HOW??
   
    //One of the following two options would be nice
    //let succes : bool = cats.retain(|cat| &cat.name == "Tom");
    //let amountDeleted : u32 = cats.retain(|cat| &cat.name == "Tom");
    cats.retain(|cat| &cat.name == "Tom");

}

Thanks a lot!

The documentation for retain says that it works by

visiting each element exactly once in the original order

And it takes FnMut, so you can just keep track of things in the closure, maybe something like

    let mut amountDeleted = 0;
    cats.retain(|cat| {
        let b = &cat.name == "Tom";
        if b {
            amountDeleted += 1
        }
        b
    });

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=406079e978b80fba12160a4e9e8ea3f0

(You can make an extension trait or function if you want to encapsulate things better, but that general idea should work.)

Edit: Oops, missed a ! -- thanks to #4 below for noticing. (Not fixed above.)

1 Like

Not op but I have some misunderstandings.
I slightly changed your playground example, adding 2 more cats and removing only one.
amountDeleted is getting tracked, okay, but cats we have only one. From total 4 cats, removing only one, and we're having only one cat.

Why is that?

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3b47b4d1ce8b1f06c62c85dceff6b8b7

retain retains everything that passes the predicate; removing all but one Cat is correct. The predicate, however, counts deleted entries wrong; it's adding one whenever an entry is not deleted, which is the opposite of what it should do. Changing the check to if !b fixes this.

1 Like

Oops! Good catch.

You could use .len() to see if the length of the Vec has changed. If it has become smaller, then retain removed elements from the Vec.

4 Likes

I think actually the cleanest solution is checking if the length of the vector changed. That's 1. explicitly mirroring the semantics of what you are trying to do, and 2. it does not require mutating additional state in the predicate function.

Thanks for the suggestions and help! This is the solution I used:

        let length_before_deletion : usize = self.cats.len();
        self.cats.retain(|&cat| cat.name != name);
        
        if length_before_deletion > self.cats.len() {
            true
        } else {
            false
        }
1 Like

Since Rust is an expression language, you can replace the `if … { … } else { … }" with just the condtion:

4 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.