0

I'm unable to understand why below code errors out ?

fn main() {
    let mut v = vec![];
    let z = &mut v;
    let y = &v;
    z.push(1);
    println!("{:?}",z); //accessing mutable borrow
}

In the above scenario, I'm not accessing y anywhere and rust should be able to compile it without any problems. I can see similar behavior happening in below code.

fn main() {
    let mut v = vec![];
    let y = &v;
    let z = &mut v;
    z.push(1);
    println!("{:?}",z); // accessing mutable borrow
}

In the above scenario, rust doesn't complain on accessing mutable borrow because immutable borrow isn't accessed anywhere. For below scenario rust complains as the code is accessing immutable borrow, which is completely understandable.

fn main() {
    let mut v = vec![];
    let y = &v;
    let z = &mut v;
    z.push(1);
    println!("{:?}",y); // accessing immutable borrow
}
akhildevelops
  • 159
  • 1
  • 8

3 Answers3

4

Even though you don't access y, creating it is enough to invalidate any existing mutable borrows.

What NLL (non-lexical lifetimes) changed is only that a reference is considered alive until its last usage, and not until the end of the scope. But a reference is always considered alive for some time.

Presumably, the behavior you want was not implemented because it's not useful: there is little value in reference that is never used.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
2

The problem isn't whether you're accessing y, the problem is that you're accessing v itself when you write &v. Note that your code works if you create y from z:

fn main() {
    let mut v = vec![];
    let z = &mut v;
    let y = &*z;
    z.push(1);
    println!("{:?}",z); //accessing mutable borrow
}

Playground

Note that this is evidenced in the error message for your original code:

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:4:13
  |
3 |     let z = &mut v;
  |             ------ mutable borrow occurs here
4 |     let y = &v;
  |             ^^ immutable borrow occurs here
5 |     z.push(1);
  |     --------- mutable borrow later used here

The error marks &v as problematic, not y.

Jmb
  • 18,893
  • 2
  • 28
  • 55
  • If that's the case, then below code shouldn't work as I'm creating &v and &mut v but it works fn main() { let mut v:Vec = vec![]; let z = &mut v; let y = &v; // z.push(1); // println!("{:?}",z); //accessing mutable borrow } https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dc1325691a4d279328726c08d2792ca9 – akhildevelops Jul 19 '23 at 01:00
  • @akhildevelops that new example works because thanks to [NLL](https://stackoverflow.com/q/50251487/7884305) `z` (and therefore the `&mut v` reference) is no longer alive when you access `&v` to initialize `y`. – Jmb Jul 19 '23 at 06:16
1

2 rules:

1- Immutable and mutable reference to the same variable cannot exist at the same time

2- The lifetime of an immutable reference lasts till its last usage

The first code fails because the immutable and mutable should not CO-EXIST (however they co-exist which is against rust borrowing rules). The variable z is mutable and its scope is from line 3 (let z = &mut v;) till line 6 (println!("{:?}",z) ). During this duration, the immutable reference should not exist. However the immutable reference exists during this scope which is on line 4 which is let y = &v;.

fn main() {
    let mut v = vec![];
    let z = &mut v; // --------------------------------- its lifetime starts here 
    let y = &v;   // y's lifetime start and ends here   |                              
    z.push(1); //                                       | z lifetime ends here                                                  
    println!("{:?}",z); 
  }
  // as you see y and z coexists

In the second code which compiles, is due to the fact that immutable and mutable does not co-exist. The variable y is a reference with a scope which starts and ends at the line 3 (since we are not using variable y afterwards). The scope of a variable starts from the line on which it is first used and ends on the line on which it is last used.

fn main() {
    let mut v = vec![];
    let y = &v;  //      y's lifetime starts and ends here
    let z = &mut v; //   z's lifetime starts here
    z.push(1);
    println!("{:?}",z); // accessing immutable borrow
}
// as you see y and z does not coexist
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
  • Why can't the scope of y in first scenario have similar behavior as that of second scenario ? – akhildevelops Jul 19 '23 at 01:23
  • @akhildevelops updated the answer – Yilmaz Jul 19 '23 at 03:36
  • There's a typo in your second code: the last line should print `z`, not `y`. – Jmb Jul 19 '23 at 06:19
  • @Yilmaz the y's lifetime end's in line 4 so why would z and y co exist in line 5 ? I'm focusing on line 5 because rust errors out if line 5-6 is present otherwise not. – akhildevelops Jul 20 '23 at 02:12
  • @akhildevelops this is the error : `error[E0502]: cannot borrow 'v' as immutable because it is also borrowed as mutable` and it points to `let y = &v;` – Yilmaz Jul 20 '23 at 02:14
  • @akhildevelops if you remove `println!("{:?}",z); ` you wil still get the error because `z`'s lifetime ends ` z.push(1); `. so `z` is live till this line from `let z = &mut v;`. while `z` is alive you got `alive` too at `let y = &v` – Yilmaz Jul 20 '23 at 02:19
  • Then in the 2nd example that I've put in my question, lifetimes of y and z should stay until line 5 right, as it happens in this case of 1st example. Why wouldn't rust complain. I'm getting confused how rust would calculate lifetimes of references. – akhildevelops Jul 20 '23 at 02:47
  • 2
    @akhildevelops References last from the time they're created until they are last used. In your 2nd example, `y` is created on line 3 and never used after that so its lifetime is only line 3. `z` is created on line 4 and last accessed on line 6 so its lifetime covers lines 4 to 6. There is no overlap between the two lifetimes and so no error. – Jmb Jul 20 '23 at 06:24