Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is rust 'pub fn func(&'a mut self)' considered "mutably borrowed" after run?

Tags:

rust

tl;dr given pub fn func(&'a mut self), why is self considered "mutably borrowed" after func has run?

Given the following minimal viable example (playground)

pub struct Struct1<'a> {
    var: &'a u8,
}

impl<'a> Struct1<'a> {
    pub fn new() -> Struct1<'a> {
        return Struct1 {
            var: &33,
        }
    }
    pub fn func(&'a mut self) -> () {
        ()
    }
}

fn main() {
    let mut s1 = Struct1::new();
    s1.func();  // point 1
                // point 2
    s1.func();  // point 3
}

results in compiler error

error[E0499]: cannot borrow `s1` as mutable more than once at a time
  --> src/test12-borrow-mut-struct-twice-okay.rs:20:5
   |
18 |     s1.func();  // point 1
   |     -- first mutable borrow occurs here
19 |                 // point 2
20 |     s1.func();  // point 3
   |     ^^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

However, at // point 2 the s1 appears to me to not be anymore borrowed. The func is done running. What could be still be borrowing self within func!? It appears func at //point 1 has relinquished control of s1.

What is still borrowing s1 at // point 3 ?


Similar questions:

  • borrow-errors-for-multiple-borrows
  • returning-a-reference-from-a-hashmap-or-vec-causes-a-borrow-to-last-beyond-the-s
  • mysterious-borrow-scope-extension
  • cannot-borrow-variable-when-borrower-scope-ends
  • why-does-this-mutable-borrow-live-beyond-its-scope)
  • mutable-borrow-seems-to-outlive-its-scope
like image 354
JamesThomasMoon Avatar asked Dec 23 '22 15:12

JamesThomasMoon


1 Answers

What is still borrowing s1 at // point 3 ?

You're telling the compiler that it's still borrowed, so it's trusting you: while the compiler verifies that your lifetimes are not too short, it doesn't really care if they're so long they make things unusable.

When you write &'a mut self, the 'a is the one declared on the impl block, and thus the one defined on the struct. &'a mut self literally desugars to:

self: &'a mut Struct1<'a>

so once you've called func() the rust compiler goes "well this is borrowed for 'a which is the lifetime associated with s1 which is 'static, so this is mutably borrowed forever, good day", and thus you get "locked out" of the structure.

In fact you can see this aliasing by trying to explicitly declare an 'a on func:

    pub fn func<'a>(&'a mut self) -> () {
        ()
    }
error[E0496]: lifetime name `'a` shadows a lifetime name that is already in scope
  --> src/main.rs:11:17
   |
5  | impl<'a> Struct1<'a> {
   |      -- first declared here
...
11 |     pub fn func<'a>(&'a mut self) -> () {
   |                 ^^ lifetime `'a` already in scope

error: aborting due to previous error

So rust is telling you in no uncertain term that within the block 'a always refers to the lifetime declared on the impl block.

The solution is to just remove the 'a, that's the wrong lifetime entirely:

    pub fn func(&mut self) -> () {
        ()
    }

In that case rustc will automatically introduce a lifetime, and since the function is not actually borrowing anything that lifetime will only extend to the function call.

like image 152
Masklinn Avatar answered Dec 26 '22 01:12

Masklinn