2

There is a struct definition as follows.

struct Test<'a> {
    t: &'a str,
}
impl<'a> Test<'a> {
    pub fn shared_borrow(&'a self) {
        println!("{}", self.t);
    }
    pub fn mutable_borrow(&'a mut self) {
        println!("{}", self.t);
    }
}

Case 1. It can pass through the borrowing check, if shared_borrow() is called before mutable_borrow().

fn main() {
    let mut x = Test { t: "test" };
    x.shared_borrow();
    x.mutable_borrow();
}

Case 2. It fails if I exchange the order of the method calls as follows.

fn main() {
    let mut x = Test { t: "test" };
    x.mutable_borrow();
    x.shared_borrow();
}

compiler's output:

error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
  --> src/main.rs:16:5
   |
15 |     x.mutable_borrow();
   |     - mutable borrow occurs here
16 |     x.shared_borrow();
   |     ^
   |     |
   |     immutable borrow occurs here
   |     mutable borrow later used here

error: aborting due to previous error

As I know, shared_borrow() and mutable_borrow() both borrow x for the entire lifetime of x, because the lifetimes are explicitly declared as 'a, which is the lifetime of x.

In Case 1, x.shared_borrow() will borrow x for the entire lifetime of x. So It shouldn't be allowed to call x.mutable_borrow() after that. I'm confused why the compiler doesn't report an error as what it does in Case 2.

openwisdom
  • 21
  • 2
  • 1
    Does this answer your question? [Why doesn't the lifetime of a mutable borrow end when the function call is complete?](https://stackoverflow.com/questions/40578224/why-doesnt-the-lifetime-of-a-mutable-borrow-end-when-the-function-call-is-compl) – Alexey S. Larionov Dec 02 '20 at 10:43
  • @AlexLarionov, I think that question is not the same as this one. For testing purposes, I tried compiling both versions of above code in rust v1.30 that doesn't have support for non-lexical leftimes. It still produced the same results. So I suppose this doesn't happen because of NLL. – Mihir Luthra Dec 02 '20 at 11:58
  • 1
    @ Alex Larionov I don't think so. Here the lifetimes are explicitly declared as 'a, which is the lifetime of x. As I know, in Case 1, x.shared_borrow() will borrow x for the entire lifetime of x. So It shouldn't be allowed to call x.mutable_borrow() after that. I'm confused why the compiler doesn't report an error as what it does in Case 2. – openwisdom Dec 02 '20 at 12:10
  • 1
    Reasonably, I also think case 1 should fail. Curious to know why it doesn't. – Mihir Luthra Dec 02 '20 at 12:26
  • 1
    tl;dr the duplicate (but actually you should read it anyway): `&'a mut self` (where `'a` is a lifetime declared in an outer scope) is virtually always a mistake. `&'a self` should be rare. Ninety-nine percent of the time, `self` should have a fresh lifetime. – trent Dec 02 '20 at 12:37
  • 1
    Re: *in Case 1, x.shared_borrow() will borrow x for the entire lifetime of x*, no, it does not. The lifetime of `x` can be "shrunk" when it is behind a shared borrow, as the duplicate's answers also explain. – trent Dec 02 '20 at 12:56
  • @trentcl Thanks a lot. I think I've got it. It's all about subtyping and variance. The lifetime in Test::shared_borrow(&'a Test<'a>) can be "shrunk", but not in Test::mutable_borrow(&'a mut Test<'a>). Right? – openwisdom Dec 03 '20 at 09:10
  • Yes, that's right. – trent Dec 03 '20 at 13:19

0 Answers0