Why how lifetime works has changed?


#1

1.0.0-nightly on 2015/02/06, I see the behavior of lifetime has changed.
Older nightly version (at least ) on 2015/01/24 passes belowing code:

fn main() {
   let mut x = &1;
   let y = 2;
   x = &y;
}

Now, compiler gives error.

main.rs:4:12: 4:13 error: `y` does not live long enough
main.rs:4       x = &y;
                     ^
main.rs:2:21: 5:4 note: reference must be valid for the block suffix following statement 0 at 2:20...
main.rs:2       let mut x = &1;
main.rs:3       let y = 2;
main.rs:4       x = &y;
main.rs:5   }
main.rs:3:16: 5:4 note: ...but borrowed value is only valid for the block suffix following statement 1 at 3:15
main.rs:3       let y = 2;
main.rs:4       x = &y;
main.rs:5   }

If I define x and y simultaneously (or y before x), it works:

fn main() {
  let (mut x, y) = (&1, 2);
  x = &y;
}

I had thought that rust only concerned the end of lifetime and the birth didn’t mattered. However, it seems do now.
I can’t find out any situation where previous behavior of lifetimes makes problem.

It seems very strict for me…


#2

Yes, this changed in PR 21657 — I can only link to it, because I don’t understand it 100% myself. Please see the link for motivation and explanation.


#3

Just put the declaration above:

fn main() {
   let y = 2;
   let mut x = &1;
   x = &y;
}

#4

If I’m interpreting the PR correctly, it appears to give individual let bindings their own distinct lifetimes, rather than having adjacent bindings share a lifetime. This makes it easier in the common case to ensure that a struct that implements Drop will only contain references whose lifetime is strictly longer than the struct itself, since any let binding that came before it will now have a longer lifetime. If a Dropable struct can contain data that has the same lifetime as itself, then it’s possible to construct it in a way where the data it references will be released before the struct itself, which could lead to memory corruption or segfaults when the destructor runs. Previously you had to use #[unsafe_destructor] to promise to the compiler that you haven’t created such a situation. but now that is no longer necessary. I’ve now removed all occurrences of #[unsafe_destructor] from my project and it still compiles fine.

In short, it’s a subtle change to solve a subtle problem with destructors.