Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing getter/setter properties in Rust

Tags:

oop

rust

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.

like image 835
ajm113 Avatar asked Feb 14 '16 10:02

ajm113


People also ask

Should you use getters and setters in Rust?

In Rust it's easier if you can avoid getters and setters (because borrow checker is more flexible with direct field access), but if you need them, they're possible to write. In Rust, since data is by default immutable, we usually use simple fields and modules can use them directly, on a "need to know" basis.

What is getter and setter properties?

What are Getters and Setters? Getters: These are the methods used in Object-Oriented Programming (OOPS) which helps to access the private attributes from a class. Setters: These are the methods used in OOPS feature which helps to set the value to private attributes in a class.

Can getter setter be static?

Yes, getters/setters can be made static based on your need.


3 Answers

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.

like image 59
DK. Avatar answered Oct 13 '22 16:10

DK.


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()
}
like image 20
W.K.S Avatar answered Oct 13 '22 14:10

W.K.S


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*/
            ),
        }
    }
}
like image 1
ADM-IT Avatar answered Oct 13 '22 14:10

ADM-IT