7

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 []?

Zhiyao
  • 4,152
  • 2
  • 12
  • 21
  • Hi there! I'm pretty sure your question is answered by [this Q&A](https://stackoverflow.com/questions/50345139/why-can-i-return-a-reference-to-a-local-literal-but-not-a-variable/50345206#50345206). Please tell us if it actually solves your problem, then we can mark this question as already answered :) – Lukas Kalbertodt Feb 26 '20 at 08:34
  • Thinking about it again: I don't think it's a perfect duplicate. It's certainly related and helps with understanding the answer to this question, but this question should get a more specialized answer. – Lukas Kalbertodt Feb 26 '20 at 08:36
  • 1
    @LukasKalbertodt Most importantly, the linked answer doesn't explain the difference between empty and non-empty slice, observed here. Since the slice here is `&mut`, the static rvalue promotion discussed in the previous answer doesn't apply. – user4815162342 Feb 26 '20 at 08:39
  • 1
    I agree! My dupe suggestion was a bit too hastily written ^_^ Looking forward to an answer for this one. – Lukas Kalbertodt Feb 26 '20 at 08:42
  • @LukasKalbertodt But anyway thank you for your quick reply! That related question link is indeed very useful to me :) – Zhiyao Feb 26 '20 at 08:48
  • 1
    This is even more difference - check this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=78edd9c45362525b7c22533f8678076d Looks like the empty array is treated as static object, while any non-empty array - as local one. – Cerberus Feb 26 '20 at 09:04
  • I don't think it is directly related array, please check : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=af626925c2c5bcafff20a752b150e326 , this problem comes with mutability I guess. – Ömer Erden Feb 26 '20 at 09:20
  • For shared reference everything is described by the link in the first comment - it's simply static promotion. – Cerberus Feb 26 '20 at 09:43

2 Answers2

5

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.

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
Cerberus
  • 8,879
  • 1
  • 25
  • 40
2

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
Ömer Erden
  • 7,680
  • 5
  • 36
  • 45