vbstb
vbstb

Reputation: 1451

Using the same lifetime for the pointer and the lifetime parameter of the struct it points to

Consider the following:

struct Str<'a> {
    s: &'a str,
}

fn f1<'a>(_: &'a mut Str<'a>) {}

fn f2<'a, 'b>(_: &'a mut Str<'b>) {}

fn main() {
    let s = "hello".to_string();
    let mut a = Str {
        s: &s,
    };

    f1(&mut a);
    // f2(&mut a);

    let t: &Str = &a;
}

f2 uses two different lifetimes, as it would when I elided them, which works fine.

At this point, I thought that the lifetime 'a refers to the lifetime of &mut a, and 'b refers to the lifetime of &s.

And then I wrote f1 which uses a single lifetime parameter, suspecting that lifetime 'a would refer to the shorter of the lifetime of &mut a and the lifetime of &s.

However, this f1 fails with the following error:

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
  --> src/main.rs:21:19 
   |
18 |     f1(&mut a);
   |        ------ mutable borrow occurs here
...
21 |     let t: &Str = &a;
   |                   ^^
   |                   |
   |                   immutable borrow occurs here
   |                   mutable borrow later used here

The error confuses me: Why is a still borrowed as mutable after calling f1?

Why does this fail, and what does the error message supposed to mean?

Upvotes: 3

Views: 294

Answers (1)

Peter Hall
Peter Hall

Reputation: 58785

Why is a still borrowed as mutable after calling f1?

fn main() {
    //                  scope of s ---\
    let s = "hello".to_string(); //   |
    let mut a = Str {            //   |
        s: &s,                   //   |
    };                           //   |
                                 //   |
    //        f1 borrows a until ---\ |
    f1(&mut a);                  // | | 
    // but it's used here \         | |
    let t: &Str = &a; //  X      // | |
                                 // X X
}

The scope of s is until the end of main. Due to the lifetime annotations on f1's arguments, the mutable reference &mut a has its lifetime tied to the scope of s, which means that f1 is borrowing a for the entire scope of s.

This is not a problem with immutable references because longer lifetimes may be coerced to shorter ones; in other words lifetimes of immutable references are covariant. But lifetimes of mutable references are invariant. That means they cannot be coerced to shorter (or longer) lifetimes.

Upvotes: 2

Related Questions