Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.3k views
in Technique[技术] by (71.8m points)

Idiomatic way to Find Struct in Struct Vec, then Perform Trait Function on that Struct in Rust

In my project I'm frequently iterating through a vector of structs to find an object by some field value, then use some trait function on that object:

pub struct Person{
    name: String,
    age: u32,
    id: u32,
}

impl Person{
    pub fn new(name: String, id_num: u32, age: u32)->Self{
        let p = Person{
            name: name,
            id: id_num,
            age: age,
        };
        p
    }
}

trait PersonTrait{
    fn printname();
    fn get_name()->String; 
    fn get_age()->u32;
    fn set_age(age: u32);
}


impl PersonTrait for Person{
    fn printname(){
        dbg!(self.name)
    }
    fn get_name()->String{
        self.name
    }
    fn get_id()->u32{
        self.id;
    }
    fn set_age(age: u32){
        self.age = age;
    }
}


fn main(){
    let my_people = vec![Person::new("Rakim".to_string(), 1, 56), Person::new("Ghostface".to_string(), 2, 56), Person::new("RZA".to_string(), 3, 56)];
    //frequently repeating this pattern of finding struct in array of structs, then doing something to that found struct

    for person in my_people.clone(){
        if person.get_id() == 1 {
            person.set_age(100);
        }
    }

    for person in my_people.clone(){
        if person.get_id() == "Rakim".to_string(){
            person.printname();
        }
    }
    
}

So the general pattern im using here is:

for x in my_objects{
    if x.id() == some_id{
        x.do_some_trait_function()
    }
}

I'd like to create a more general function to make this syntax simpler, something like:

//not sure what the correct syntax would be here, or how you might pass a trait function as an argument
fn find_then_do_trait_function(obj_list: Vec<Person>, id: u32, trait_function: my_trait_function()){ 
    for x in obj_list(){
        if x.get_id() == id {
            //use my trait function on x
        }
    }
}

How might I do this? I know I could create an enum for every trait function, then match that enum, but that also seems pretty verbose.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

There's nothing unique about trait functions. You've identified a very common pattern which can be split into two pieces: we want to filter a vector and then perform some operation on each matching element. We can define a function that takes two closure arguments to do this for us.

fn search_and_call<T>(obj_list: &mut Vec<T>,
                      mut condition: impl FnMut(&mut T) -> bool,
                      mut func: impl FnMut(&mut T) -> ()) {
    for x in obj_list {
        if condition(x) {
          func(x);
        }
    }
}

func can be any closure. That closure might call a trait function, or it might print to the screen, or do any number of things. The writer of the above function needn't care; it's all the same as far as we're concerned. Sample usage:

let mut v = vec!(1, 2, 3, 4);
search_and_call(&mut v, |x| *x % 2 == 0, |x| println!("{}", *x));

It's worth noting, however, that Rust's excellent Iterator trait defines a ton of useful functions, and we can get this behavior for free, without even touching a for loop.

let mut v = vec!(1, 2, 3, 4);
v.iter().filter(|x| *x % 2 == 0).for_each(|x| println!("{}", *x));

.iter() gets an iterator over our vector, .filter(...) produces a new iterator that selects certain elements based on our condition, and .for_each(...) calls a function on all elements which remain after the filter.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...