0

The following rust code creates a struct "Foo" that models a person with a name and age. Two mutator methods exist: "set_name" and "set_age". The "set_name" uses "self as a reference" and "set_age" "self as a value".

The ultimate goal here is to understand how these two approaches for methods can be used against a Foo element stored in a vector.

The question boils down to the end of the code where, in comments, I have a failed attempt where I am trying to make "Jane Doe" 19 years old now that she has been moved into a vector (and college!) owned by "bar2". The code above that was able to make her 18 when she was in a the variable "bar1" using shadowing.

When the statement which fails is uncommented the compiler emits the following error:

error[E0507]: cannot move out of index of `Vec<Foo>`
  --> src/main.rs:36:15
   |
36 |     bar2[0] = bar2[0].set_age(19); // Nope, try again!
   |               ^^^^^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait

error: aborting due to previous error

Thank you all very much for your thoughts and insight.

struct Foo {
    name: String,
    age: u32,
}
impl Foo {
    fn display(self: &Foo) {
        println!("age={}, name={}", self.age, self.name);
    }
    fn set_name(&mut self, name: &str) {
        self.name = name.to_string();
    }
    fn set_age(mut self, age: u32) -> Foo {
        self.age = age;
        Foo{ name: self.name, age: self.age }
    }
}

fn main() {
    let mut foo = Foo{ age: 0, name: "Nobody".to_string() };
    foo.display();

    foo.set_name("Jane Doe in High School");
    let foo = foo.set_age(17);
    foo.display();

    let mut bar1 = foo;
    bar1.set_name("Jane Doe Can Vote");
    let bar1 = bar1.set_age(18);               // This works!
    bar1.display();

    let mut bar2: Vec<Foo> = Vec::new();
    bar2.push(bar1);
    bar2[0].set_name("Jane Doe In College");

    // Now make Jane Doe a year older.
    // bar2[0] = bar2[0].set_age(19); // Nope, try again!

    bar2[0].display();
}

Running this code in the current form produces the following output:

$ cargo run
...<banner stuff>...

age=0, name=Nobody
age=17, name=Jane Doe in High School
age=18, name=Jane Doe Can Vote
age=18, name=Jane Doe In College

So the goal is to replace my failed comment code so that the last line reads:

age=19, name=Jane Doe In College
ChrisK
  • 61
  • 3
  • `Nope, try again!` - is that what the language told you? or did it give some _actual_ error? Anyway, why not just do `bar2[0].set_age(19);`? Why try to assign that to `bar2[0]` too? – underscore_d Apr 26 '21 at 15:18
  • @underscore_d that's what they do, I think – Sergio Tulentsev Apr 26 '21 at 15:20
  • then why not also do that for `set_name()`, which already works without it? Granted, I don't work with Rust, but assigning `bar2[0] = bar2[0].set_age(19)` looks weird and unnecessary. and their `set_age()` seems not to return `self`, but rather a copy thereof - which I suppose is the proximal problem – underscore_d Apr 26 '21 at 15:20
  • "Nope, try again" is certainly not what the compiler is telling you. – Sergio Tulentsev Apr 26 '21 at 15:45
  • Sergio, Thank you for pointing out that adding the actual error would be helpful here. I agree. Sorry I should have thought of that. My comment "Nope..." was simply meant to be a marker for my measly attempt. I honestly did not think it was correct, and that noone else would have tried it. So taking your thoughtful suggestion I added the error when the incorrect statement is un-commented. – ChrisK Apr 27 '21 at 14:36

2 Answers2

1

The problem here isn't in the set_age method (well, there's problems with that too but those are stylistic rather than programmatic).

The problem is that you are trying to "move" an object out of a vector, which isn't allowed.

Listen to the Rust compiler. It tells you exactly that. Then google that error message to see what's going on here.

Basically, because set_age wants to consume self, it will have to move ownership out of the vector and into the method. But moving individual items out of a vector isn't allowed: What does "cannot move out of index of" mean?

cadolphs
  • 9,014
  • 1
  • 24
  • 41
  • Thank you for the helpful answer!, Btw/ My goal here was not to identify good style or idiomatic approaches on the "impl" side. I wanted to know how, given a method written that way, what options are available to the problem I'm describing. Imagine a world where I was handed the code someone else has written and whatever the equivalent of C++, class headers in Rust, required committee approval, but user code did not. So if the only approach is to implement a new mutator, that is good to know. It's also good to know what options are available in the user code. – ChrisK Apr 27 '21 at 14:52
1

Looking at the actual compiler error, we see

error[E0507]: cannot move out of index of `Vec<Foo>`
  --> src/main.rs:37:15
   |
37 |     bar2[0] = bar2[0].set_age(19);
   |               ^^^^^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait

To be able to do set_age, the object has to be moved out of its previous location. When it's a variable of its own, it's easy: the compiler just marks the previous location is invalid. Here, however, the value resides in a container. What should happen if you move it out? You can't move the entire container. And if you don't, what do you leave in place of the moved value?

One way to work around this is to make the struct Clone, so we consume a copy and don't leave the vector in an invalid state.

#[derive(Clone)]
struct Foo {
    name: String,
    age: u32,
}


bar2[0] = bar2[0].clone().set_age(19);
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • Sergio. Thank you very much! This answer looks like the best solution to the problem. Again, I apologize for not including the error. But this really helps me understand something I've found confusing and that is the benefits to using self as a reference vs. self as a value. It appears that reference is much better for this particular use case, but your answer may be the shortest fix for getting the desired results. – ChrisK Apr 27 '21 at 17:56