3

Consider the following program. The particularity is that the signature of foo and foo_mut require self to be borrowed for the lifetime 'a, which is also the type parameter of T<'a> in the functions test1<'a> and test2<'a>.

test2 compiles fine, but test1 fails to compile. Why? Don't they have the same lifetime semantics? I expect both the functions to be rejected, because both t.foo() and t.foo_mut() should borrow t for 'a, thus until the end of test1 and test2.

struct T<'a>(&'a u32);

impl<'a> T<'a> {
    fn foo(&'a self) {}
    fn foo_mut(&'a mut self) {}

    fn bar_mut(&mut self) {}
}

fn test1<'a>(mut t: T<'a>) {
    t.foo_mut();
    // The following call is rejected because `t` is still borrowed.
    t.bar_mut();
}

fn test2<'a>(mut t: T<'a>) {
    t.foo();
    // The following call is allowed; it seems that `t` is not borrowed.
    t.bar_mut();
}

fn main() {}
error[E0597]: `t` does not live long enough
  --> src/main.rs:11:5
   |
10 | fn test1<'a>(mut t: T<'a>) {
   |          -- lifetime `'a` defined here
11 |     t.foo_mut();
   |     ^----------
   |     |
   |     borrowed value does not live long enough
   |     argument requires that `t` is borrowed for `'a`
...
14 | }
   | - `t` dropped here while still borrowed

error[E0499]: cannot borrow `t` as mutable more than once at a time
  --> src/main.rs:13:5
   |
10 | fn test1<'a>(mut t: T<'a>) {
   |          -- lifetime `'a` defined here
11 |     t.foo_mut();
   |     -----------
   |     |
   |     first mutable borrow occurs here
   |     argument requires that `t` is borrowed for `'a`
12 |     // The following call is rejected because `t` is still borrowed.
13 |     t.bar_mut();
   |     ^ second mutable borrow occurs here

Link to Rust Playground

Federico
  • 1,925
  • 14
  • 19
  • tl;dr: the `self` borrow you take to call `foo` or `foo_mut` doesn't need to have the lifetime `'a`. It works with `foo` because of variance, but the best solution is to just omit the unnecessary lifetime. [Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=665375c90b23715af61de84ea4804bc6) – trent Apr 25 '19 at 13:09
  • 2
    If you want to dig deep into the reasons behind those semantics, take a look at [the variance of both reference kinds](https://doc.rust-lang.org/nomicon/subtyping.html). In particular, `&mut T` is *invariant* over `T`, while `&T` is *covariant* over `T`. This means that the compiler is forced to borrow `t` for the full lifetime `'a` in `test1`. In `test2`, the compiler is allowed to shorten the lifetime (because of the covariance) so that the borrow is shorter. – Lukas Kalbertodt Apr 25 '19 at 13:29

0 Answers0