0

I have a struct containing a mutable reference and an owned value. I'm trying to see if I can implement some getters for these values. To my surprise, getx compiles but gety doesn't. Given that neither &mut String nor String implements Copy, why would the compiler allow getting a &mut String but not a String?

struct S<'a> {
    x: &'a mut String,
    y: String,
}
impl<'a> S<'a> {
    fn getx(&mut self) -> &mut String {
        self.x // Ok
    }
    fn gety(&mut self) -> String {
        self.y // Err
    }
}

Error messages:

error[E0507]: cannot move out of self.y which is behind a mutable reference

move occurs because self.y has type String, which does not implement the Copy trait

TSK
  • 509
  • 1
  • 9
  • 3
    `&mut` can be "implicitly reborrowed". Here are some useful resources for understanding: [1](https://stackoverflow.com/questions/62960584/do-mutable-references-have-move-semantics), [2](https://stackoverflow.com/questions/30535529/variable-binding-moving-a-mut-or-borrowing-the-referent), [3](https://github.com/rust-lang/reference/issues/788) – PitaJ Nov 04 '22 at 20:25
  • @PitaJ Thanks bro they once had some documentation [here](https://doc.rust-lang.org/1.12.0/nomicon/references.html#liveness) – TSK Nov 04 '22 at 21:08

1 Answers1

1

In the gety example you move out of the String. You can't do that because the ownership would be given to the caller, leaving the original struct in a partial state (it still needs to be usable because you only borrow yourself mutably via &mut self).

As @PitaJ correctly said, the getx example works via implicit reborrowing. This concept is not documented enough (and there is an issue for it), but it can be explained pretty easily:

When the compiler knows the type of a variable to be a (mutable) reference, it can be automatically borrowed (mutably). In this case a reborrow occurs, meaning that the expression first gets dereferenced like this:

fn getx(&mut self) -> &mut String {
    &mut *self.x
}

Or, with explicit lifetime annotations:

fn getx<'s>(&'s mut self) -> &'s mut String {
    &mut *self.x
}

This works, because it returns a mutable reference to the String's contents, but not the mutable reference stored in the container. Because this returns a exclusive reference to the inner String, it is not possible to change the String in another way while this reference is in scope. This is one of the most fundamental aspects of Rust's ownership model.

leo848
  • 637
  • 2
  • 7
  • 24