54

I'm writing a very simple getter/setting model that I would like to start using in Rust for simplicity reasons using struct and impl.

struct Person {
    firstName: String,
    lastName: String,
}

impl Person {
    fn get_first_name(&mut self) -> String { return self.firstName; }
    fn get_last_name(&mut self) -> String {  return self.lastName; }

    fn set_first_name(&mut self, x: String) { self.firstName = x; }
    fn set_last_name(&mut self, x: String) { self.lastName = x; }

    fn default() -> Person {
        Person {firstName: "".to_string(), lastName: "".to_string()}
    }
}

fn main() {
    let mut my_person : Person = Person{ ..Person::default() };

    my_person.set_first_name("John".to_string());
    my_person.set_last_name("Doe".to_string());

    println!("{}", my_person.firstName);
    println!("{}", my_person.lastName);
}

When I run this snippet I get the following error.

src\main.rs:7:53: 7:57 error: cannot move out of borrowed content [E0507]
src\main.rs:7     fn get_first_name(&mut self) -> String { return self.firstName; }
                                                                  ^~~~
src\main.rs:8:53: 8:57 error: cannot move out of borrowed content [E0507]
src\main.rs:8     fn get_last_name(&mut self) -> String {  return self.lastName; }
                                                                  ^~~~
error: aborting due to 2 previous errors
Could not compile `sandbox`.

Can someone point out the mistake to me since I'm very new to Rust?

Tips on writing this snippet better would be accepted too. I'm always looking for easier/faster readability.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ajm113
  • 936
  • 1
  • 8
  • 18
  • 6
    Why do you think you need getters and setters? Why not `struct Person { pub first_name: String, pub last_name: String, }` which is quite simpler? – Matthieu M. Feb 14 '16 at 15:21
  • 3
    Note that sometimes getters and setters can still be useful, e.g. when you define a default method in a trait, since for now you can't define/access any fields in a trait. See https://internals.rust-lang.org/t/fields-in-traits/6933 – xji Apr 12 '18 at 06:00

4 Answers4

71

Ok, the specific problem here is not being able to move out of borrowed content. This has been answered numerous times before under a variety of conditions, not to mention the chapter on the subject of ownership in the Rust Book.

The more interesting one is about getters and setters. Yes, you can write them in Rust, but they may not be the best choice.

Before I go on, I just want to note that there is absolutely no reason to require &mut self on a getter... unless you intend to modify the value as part of removing the value, but then you're not really dealing with a getter any more.

Secondly, you should not clone in a getter. This is hugely wasteful if all the user wants to do is, for example, read from the value. It's better to return an immutable borrow, from which the user can clone if they need to.

Anyway, if you're writing these because you want some kind of logic run in order to validate new values, keep using setters. Otherwise, you could do something like this:

#[derive(Default)]
struct Person {
    first_name: String,
    last_name: String,
}

impl Person {
    // Immutable access.
    fn first_name(&self) -> &str {
        &self.first_name
    }
    fn last_name(&self) -> &str {
        &self.last_name
    }

    // Mutable access.
    fn first_name_mut(&mut self) -> &mut String {
        &mut self.first_name
    }
    fn last_name_mut(&mut self) -> &mut String {
        &mut self.last_name
    }
}

fn main() {
    let mut my_person = Person::default();

    *my_person.first_name_mut() = String::from("John");
    *my_person.last_name_mut() = "Doe".into();

    println!("first_name: {}", my_person.first_name());
    println!("last_name: {}", my_person.last_name());
    
    // Can't do this efficiently with getter/setter!
    {
        let s = my_person.last_name_mut();
        s.truncate(2);
        s.push('w');
    }

    println!("first_name: {}", my_person.first_name());
    println!("last_name: {}", my_person.last_name());
}

This gives users more-or-less direct access to the fields, without actually giving them direct access to the fields. In addition to writing new values, this also allows users to mutate existing values in-place, which can be important for large, heap-allocated things.

In addition, I made a few other changes:

  • You can just mechanically derive Default; there's no reason in this case to write it yourself.

  • Conventional style is snake_case for fields.

  • The way you created the Person was needlessly roundabout.

Stargateur
  • 24,473
  • 8
  • 65
  • 91
DK.
  • 55,277
  • 5
  • 189
  • 162
  • Isn't it more correct to use `&str` instead of `&String`? – W.K.S Feb 14 '16 at 11:18
  • @W.K.S, funny I actually had the same question. I remember reading something along the lines of using str instead, but I don't remember... – ajm113 Feb 14 '16 at 11:27
  • 1
    @ajm113 Normally, yes. *However*, in this case, you *also* have mutable access to the `String`, and there is one thing you can do with a `&String` that you cannot do with a `&str`: check the capacity. Plus, *in general*, the two pointers will be of the same type. – DK. Feb 14 '16 at 12:04
  • 2
    @DK.: What is the benefit of handing over a mutable reference to your field over just making the field public? – Matthieu M. Feb 14 '16 at 15:20
  • 2
    @MatthieuM. About the only things it buys you are that users can't move out of it, and you can change where it's stored on the fly. Beyond that... *shrugs* – DK. Feb 14 '16 at 17:09
  • setter pattern will still be better for the setter to be really encapsulated. If you wanted any other action than just the set, like debug logging or some internal counter, returning &mut would cause some refactoring. – Leo Dutra Oct 25 '20 at 06:40
  • "there is absolutely no reason to require &mut self on a getter" - lazy initialization (that isn't quite invisible to the outside world)? – John Dvorak Dec 08 '20 at 11:53
  • @JohnDvorak - I think in the case of lazy initialization you could still avoid resorting to &mut self on the getter by using things like Cell or RefCell. –  Dec 18 '20 at 21:09
9

Your getter method borrows self. When your return self.name, you're moving name from a borrowed reference which is not allowed. You should return a copy of name.

Also, You do not need to pass a mutable reference to self in the getter methods since you are not modifying the internal struct.

Therefore, your getter methods should be:

fn get_first_name(&self) -> &str {
    self.first_name.as_str()
}
fn get_last_name(&self) -> &str {
    self.last_name.as_str()
}
Stargateur
  • 24,473
  • 8
  • 65
  • 91
W.K.S
  • 9,787
  • 15
  • 75
  • 122
  • Hey thanks for the very helpful feed back! Makes sense to do everything directly, now you put it that way. Obviously I came from a very heavy OOP background w/ PHP/Java. So in this case I need to start thinking more simply functional in this case is pretty much what I need to be doing for the most part. Unless we are talking about adding functionality to the struct. – ajm113 Feb 14 '16 at 10:56
  • @ajm113 This has nothing to do with functional vs OOP. It is to do with memory management in Rust. There is no need to return a &mut self on a getter as getting does not change the any values. (mut implies mutable). – Dirk R Jul 22 '23 at 19:57
1

As the compiler points out, the mistake you are making is trying to move a value out of a variable that is behind a reference, but that explanation alone can be difficult to grasp on its surface without understanding why the move is occurring, and why it is "behind a reference", which IMO is not so obvious to the untrained eye so I'll try to explain that.

References are distinct types

In the signature of both get_first_name() and get_last_name() you have declared the parameter as &mut self. This means that in the body of these functions, the variable self has type &mut Person (pronounced "mutable reference to Person"), not Person. These two types are distinct and as with any pair of distinct types, there are different rules governing how you can use them, so it is important to think of them as different types.

Moving Rules

The &mut Person and Person types share a lot of similarities in how they can be used (specifically, those related to the Person API), but one major difference between how they can be used is that you can move values out of the fields of a Person instance, but not out of the fields of a &mut Person or a &Person: such types are said to be "behind a reference" or "borrowed"

Non Copy values have move semantics

Any value that has a type which does not implement the Copy trait moves whenever it is

  • assigned to a variable
  • passed into a function or method
  • returned from a function or method

The body of your function is return self.firstName and this is trying to return the value in the firstName field to the caller. This value is of type String, which does not implement Copy, therefore this line means "move the value stored in the firstName field out of the struct instance to the caller of get_first_name(). But, since self is of type &mut Person, this is not allowed.

RBF06
  • 2,013
  • 2
  • 21
  • 20
1

You can try to implement some kind of GettterSetter stuff. For example:

struct Person {
    first_name: Gettter<String>,
    last_name: GettterSetter<String>,
}

impl Person {
    fn new() -> Self {
        Self { 
            first_name: Gettter::new(|_| /*do_your_stuff_on_get*/),
            last_name: GettterSetter::new(
              |_| /*do_your_stuff_on_get*/,
              |val| /*do_your_stuff_on_set*/
            ),
        }
    }
}
ADM-IT
  • 3,719
  • 1
  • 25
  • 26