Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the lifetime of `&mut []` treated specially during lifetime analysis in Rust? [duplicate]

Tags:

rust

The example code below is somewhat crafted but illustrates my major concern. The code compiles perfectly.

struct SliceWrapper<'a>(&'a mut[i32]);

impl<'a> SliceWrapper<'a> {
    fn clear(&mut self) {
        self.0 = &mut [];
    }
}

fn main() {
    let slice = &mut [1, 2, 3];
    let mut wrapper = SliceWrapper(slice);
    wrapper.clear();
}

The line self.0 = &mut []; works but is very strange if we look at their lifetimes: a reference to a local variable is assigned to self.0, which lives beyond the method call clear().

What makes it more confusing is that if I change that line to self.0 = &mut [0];, then the compiler will throw me an error saying: creates a temporary which is freed while still in use.

So I guess the Rust compiler treats the lifetime of &mut [] differently. Is that true? What is the precise lifetime rule for &mut []?

like image 620
Zhiyao Avatar asked Feb 26 '20 08:02

Zhiyao


2 Answers

Empty array is indeed special-cased by the compiler.

This answer describes the similar yet different case with shared references to constexpr-values. For this case, there is an RFC to make this constexpt-values "promoted to static", i.e. allocated in static memory and not at stack, so that they will live outside the function they're defined in.

And, inside this RFC, we have the following statement:

the compiler already special cases a small subset of rvalue const expressions to have static lifetime - namely the empty array expression:

let x: &'static [u8] = &[];

As for the mutable - or, as will be more relevant here, exclusive, or unique, references, RFC states the following:

It would be possible to extend support to &'static mut references, as long as there is the additional constraint that the referenced type is zero sized.
...
The zero-sized restriction is there because aliasing mutable references are only safe for zero sized types (since you never dereference the pointer for them).

This part seems to be not implemented, since this code is invalid:

fn main() {
    let _: &'static mut [()] = &mut [()]; // fail, reference to local
}

although [(); 1] is ZST, as one can check with std::mem::size_of.

like image 68
Cerberus Avatar answered Sep 25 '22 04:09

Cerberus


The line self.0 = &mut []; works but is very strange if we look at their lifetimes: a reference to a local variable is assigned to self.0, which lives beyond the method call clear().

This is because Rust has rvalue static promotion feature which is explained in another post which @Lukas Kalbertodt pointed in comment,

Please check : Why can I return a reference to a local literal but not a variable?


For the lifetime error in this code:

self.0 = &mut [0];

This is not supported by static promotion from the RFC :

It would be possible to extend support to &'static mut references, as long as there is the additional constraint that the referenced type is zero sized.

This is a constraint for static mutability in this feature. It is possible for immutable types.

The code below will work fine for immutable slices.

let _: &'static [u32] = &[1, 2, 3]; // Possible

let _: &'static mut [u32] = &mut [1, 2, 3]; //Not possible
like image 44
Ömer Erden Avatar answered Sep 24 '22 04:09

Ömer Erden