For-loop and &self trouble

Hi! I am loving rust so far, but having a bit of trouble understanding why &self does not work in my fn using a for loop, but self works... Please take a look at the following code:

pub struct Persons {
    pub persons: Vec<Person>
}

impl Persons {
    pub fn search(&self, query: String) -> Option<Vec<Person>> {
        let mut results: Vec<Person> = vec![];
        for person in self.persons {
            if person.first_name.contains(query.as_str()) {
                results.push(person);
            }
        }
        return if results.is_empty() {
            None
        } else {
            Some(results)
        }
    }
}

Creates error: move occurs because self.persons has type std::vec::Vec<data::person::Person>, which does not implement the Copy trait.

I simply don't understand why a memory action would be used here, I simply want to go through all entities in a vec derived though self.

I am going to call the search function implemented in Persons from this function:

pub fn search(query: String, persons: &Persons) {
	println!("--- Contacts by search: {} ---", query);
	let result = persons.search(query.clone());
	if result.is_none() {
	    println!("Could not find any contacts by {}", query);
	    return;
	}
}

Does anyone know how I can fix this, or how I can do this in a better way? Thanks.

for person in self.persons { ... }

This consumes (aka moves, transfers ownership, etc) self.persons, which would leave self in an invalid state (the persons field would be inaccessible) if this was allowed to compile.

You don't want to transfer ownership of Person from self.persons to the return value, you want to copy them!

To fix the initial error, use:

for person in &self.persons { ... }

Now the type of person is &Person which will give you a second error:

error[E0308]: mismatched types
  --> src/lib.rs:15:30
   |
15 |                 results.push(person);
   |                              ^^^^^^ expected struct `Person`, found `&Person`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

I assume the Person type includes non-Copy types, like String, so you will probably want to use Clone here...

#[derive(Clone)]
pub struct Person {
    first_name: String,
}

Now we can clone &Person into the result vector:

results.push(person.clone());

playground

3 Likes

Thank you! :slight_smile:

And in case you're inclined, you can also replace the for loop with the following which should be equivalent:

let results: Vec<Person> = self.persons
    .iter()
    .filter(|person| person.first_name.contains(query.as_str()))
    .cloned()
    .collect();
1 Like

This is probably cleaner rust, I am not used to iter :slight_smile: