Access field in struct using getters

Hi, Can anybody help mi understand how to work with struct like this:

#[derive(Debug)]
pub struct Foo {
    s1: Option<String>,
    s2: Option<String>,
}

impl Foo {
    fn get_s1(&mut self) -> &mut String {
        self.s1.as_mut().unwrap()
    }
    fn get_s2(&mut self) -> &mut String {
        self.s2.as_mut().unwrap()
    }
}

fn main() {
    let mut foo = Foo {
        s1: Some("s1".to_string()),
        s2: Some("s2".to_string())
    };
    
    foo.get_s1().push_str(foo.get_s2());
    dbg!(foo.get_s1());
}

I get error:

  Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> src/main.rs:22:27
   |
22 |     foo.get_s1().push_str(foo.get_s2());
   |     ---          -------- ^^^ second mutable borrow occurs here
   |     |            |
   |     |            first borrow later used by call
   |     first mutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

The idea is to have a struct with tcpstream and buffer and read from tcpstream passing buffer as reference

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5e0201b77a2d9c1a2749ef070c89fe9a

The get_s1 method borrows everything in Foo mutably even though it only accesses s1 because the compiler does not look inside other functions when compiling a function (in this case main). It looks only at the signature, and get_s1 has a signature that would allow it to access s2 even if it doesn't. The same story applies to get_s2.

1 Like

Hmm, now it is clear to me how the compiler works in this case. Thanks.

But what is the pattern to dig out variables from complicated structs?

Or maybe the better way is to not use "getters" at all, but "setters".
And instead of get_s1 and get_s2 use:

    fn concat_s1_s2(&mut self) {
        self.s1.as_mut().unwrap().push_str(self.s2.as_ref().unwrap());
    }

If you are letting people read and write the field anyway, why not just make it public so they can do the concatenation directly?

1 Like

Yes, but I thought that it would be less repetitive to have getter function to dig out variable and use it in other places of the program. In my example variable is just behind Option, but I can imagine more complicated cases.

Often "more complicated cases" will have other requirements such that offering &mut access isn't a satisfactory solution anyway. For example, if there is anything you want to update after the value is changed, anywhere in that complicated structure, you need a setter, more or less, rather than returning &mut.

4 Likes

So for now i will keep case by case strategy, and try not raise the problem before it accually happens.
Thanks You all for replies.

That's just something you have to accept: the determined Real Programmer can write FORTRAN programs in any language, of course, but it's really hard to write Java, JavaScript, or C++ programs in Rust.
Not entirely impossible but hard. Very often when you see that Rust is trying to fight you it's better to go back, forget about your knowledge of other languages, think about your problem in layman terms and you may find a much more readable solutions.
Although some things are just hard in Rust. Like linked lists, e.g.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.