Remix.run Logo
asQuirreL 2 days ago

Rust already supports the kind of behaviour you are describing for borrows, because of non-lexical lifetimes. Code like the following now compiles:

    fn main() {
      let mut x = 42;
      let y = &x;
      println!("{y}");
      let z = &mut x;
    }
Even though y's scope overlaps with z's, and they introduce conflicting borrows, this code compiles because the compiler treats y's borrow as dead after its last use (this has been true since Rust Edition 2018, so for quite some time now). If you move the println after the mutable borrow then it fails to compile.

However values whose types have Drop are another matter. They are treated as if there's an explicit call to their drop function at the end of their lexical scope which pins their lifetime. This is intentional and desirable precisely because of the guard pattern (like for mutexes).

If you didn't have that guarantee, at worst your mutex's guard object would be immediately dropped because it's never referenced after it's created, or at best it would be very tricky to understand what the protected critical region is.

Panzerschrek 2 days ago | parent [-]

> If you didn't have that guarantee, at worst your mutex's guard object would be immediately dropped

For named local variables it's a different story. They should remain alive until the end of their lexical scope. But for unnamed temporaries created in expressions different rules should apply - as soon as there is no reference to such temporary, it should be destroyed.

asQuirreL a day ago | parent [-]

Okay, I see. The issue you are running into is specifically mentioned in this article about how Rust currently does lifetime extension:

https://smallcultfollowing.com/babysteps/blog/2023/03/15/tem...

Typically, a temporary's lifetime is bounded by the statement it is in, but for the subject of a match, this extension overlaps with all its arms even if the temporary borrow is not used after the subject is evaluated (i.e. you borrowed, you read and copied a field out of the borrow).

The issue seems to be that this is a syntactic transformation, but the expected behaviour requires type information, so you can tell whether to extend the temporary's lifetime by whether that lifetime leaks the immediately containing scope.

This is kind of similar to how type parameter unification in Hindley-Milner works. There's even an analogy made between the two things here:

https://okmij.org/ftp/ML/generalization.html#gen-mismanageme...