What is the difference between b""[..] and &[]?

What does this work

fn test() -> &'static mut [u8] {
    &mut []
}

fn main() {
    assert_eq!(test().len(), 0);
}

and this

fn test() -> &'static mut [u8] {
    &mut b""[..]
}

fn main() {
    assert_eq!(test().len(), 0);
}

doesn't?

b"" is a literal - it's baked into the program itself, and can't be modified (you can't take a &mut to it.) [] is a [u8; 0], which can be modified (although it has no elements, so you can't actually do anything with it.)

e: To be more specific about the byte string, it's a &'static [u8], which is why you can't get a &mut to it - all literals are immutable statics.

1 Like

Take a different approach for looking at this; say that our return values were actually populated;

fn test_1() -> &'static mut [u8] {
    &mut [65, 66, 67]
}

and

fn test_2() -> &'static mut [u8] {
    &mut b"ABC"[..]
}

Then we can understand that the first one is illogical because we can't mutate things that are statically built into the program (Apart from static mut X, but that's different) so therefore if we say that &mut [] is essentially (*mut data, len) and data = nullptr; len = 0; (If we had a notion of nullptr). But on the other hand, we look at b"" and say that we want to index into a byte array. The fact that the byte array is of size 0 isn't something that rust will take into account and will instead be optimized away, meaning that rust just sees a [u8; n] array. Which is the same as my test_1 because rust sees that we're trying to modify static memory, which is illegal.

1 Like

&[] is literal as are &[65, 66, 67] and b"ABC". The last two are also "baked" into the binary. But I am not sure if &[]/&mut [] and b"" are literally backed, since they are empty and the pointers will indeed be nullptr (or rather some dangling pointer).

b"" has the type of &'static [u8; 0].
&[] has the type of &[_; 0] and &mut [] has the type of &mut [_; 0].

I think this has something to do with the types in play:

  1. In case &mut [u8] as &'static mut [u8] we do a lifetime extension, i.e. &mut [u8] to &'static mut [u8]
  2. OTOH, b"" as &'static mut [u8] is casting from &'static [u8] to &'static mut [u8].

Even though that generally extending the lifetime of a reference like in the pt1 is generally illigal, it works for literals. (However, this hints that it might have something to do with empty arrays modulo the warning)

I think the simple answer (which doesn't appeal to any sort of implementation detail) is that:

  • When you write b"", the compiler has already committed to the the non-mut type &[u8; 0].
  • In contrast, when you write [], the compiler isn't yet sure whether it will be mut or not, so it allows you to apply either & or &mut to it.

In other words, &mut b""[..] fails for the same reason that &mut (&[])[..] fails.

3 Likes