Ownership and methods

Please clarify if my understanding of ownership with respect to methods is correct as described below:

use chrono::{NaiveDate};

#[derive(Debug, Clone)]
pub struct Person {
  pub name: String,
  pub birthday: NaiveDate
}

#[derive(Debug)]
pub struct Family {
  pub members: Vec<Person>
}

impl Family {
  pub fn list_members(&self) -> Vec<Person> {
    let mut my_members: Vec<Person> = Vec::new();
    for member in self.members {
      my_members.push(member);
    }
    my_members
  }
}

The above code fails with error message:

   |
17 |     for member in self.members {
   |                   ^^^^^^^^^^^^ cannot move out of borrowed content

This happens because by defining the method list_members as:

pub fn list_members(&self) -> Vec<Person>  // self is a reference

we are just borrowing the "instance" (is that correct term for Rust?) of the struct and therefore we cannot move any of the data referenced by the fields in the struct.

But when we change the signature to:

pub fn list_members(self) -> Vec<Person>  // self is no longer a reference

we are taking ownership of the instance and therefore can freely move the data referenced by it's fields, correct?

Your understanding is pretty much correct!

When you do for x in vec (as opposed to for x in &vec or for x in &mut vec), you move all of the elements out of the vector as you iterate over it, and then drop the vector at the end of the loop. You can't move something out of a shared reference (such as &self), so this doesn't work.

1 Like

Thanks, @17cupsofcoffee!

So any access for family.members will (as expected) fail after call to list_members(self) . See example below:

use routines::{Person, Family};
use chrono::NaiveDate;
use std::str::FromStr;

pub fn main() {
  let people = vec![
    Person {
      name: String::from("Brain"),
      birthday: NaiveDate::from_str("2000-01-01").unwrap()
    },
    Person {
      name: String::from("Robert"),
      birthday: NaiveDate::from_str("1997-01-01").unwrap()
    }
  ];

  let fam = Family { members: people };

  println!("{:?}", fam.list_members());
  println!("{:?}", fam.members);    // The line fails as expected.
}

My question here: Is there any way to preserve family.members after a call to list_members(self) and have the last line of the code above compile and work?

My immediate thought is we could use cloning with reference in for loop:

for member in &self.members {
    my_members.push(member.clone());
}
my_members

But isn't cloning bad for performance?

Ah, your issue makes a lot more sense in context!

Cloning would work, but in this case, you're probably best off returning a slice, instead of a whole new Vec. A slice is a 'view' into a piece of memory (e.g. a Vec or an array) that is owned by something else.

use chrono::NaiveDate;

#[derive(Debug, Clone)]
pub struct Person {
    pub name: String,
    pub birthday: NaiveDate,
}

#[derive(Debug)]
pub struct Family {
    pub members: Vec<Person>,
}

impl Family {
    pub fn list_members(&self) -> &[Person] {
        // &Vec<T> can coerce into &[T]
        &self.members
    }
}

Have you read the Rust book? This is covered in Chapter 4.3 - I'd definitely recommend reading the whole thing if you have time, it's a good introduction to these kinds of concepts :slight_smile:

Well, I was rummaging through Chapter 4.1 and 4.2 and never bothered with 4.3! :sweat_smile:

Thanks! :slight_smile:

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