Why doesn't this code compile #2?

pub struct Entry_Key([u8; 40]);

impl Entry_Key {
    pub fn data(&self) -> &[u8; 32] {
        &self.0[8..40]}

I feel like I am missing something very fundamental. To me [8 .. 40] is obviously 32 entries. So why is it not a &[u8; 32] ?

Aside: #2 since "title has already been used here"

The compiler doesn't analyze the size. It could have been anything. You can do:

pub fn data(&self) -> &[u8; 32] { self.0[8..40].try_into().unwrap() } // Cannot fail

The optimizer is likely to remove the check.

5 Likes

Godbolt confirms that the check is indeed optimized away in release mode

1 Like

Also, indexing an array defers to indexing a slice, which uses this implementation that returns another slice.

You would need const generics that could do math and monomorphized independently for every combination of usizes to support it (at least naively/without more magic).

1 Like

Why does

fn test_00() {
    let mut x = [0_u8; 20];
    let y: &mut [u8; 10] = x[0..10].try_into().unwrap();}

not compile? Here I want a mut ref instead of a ref.

Inference problem. Be more specific:

let y: &mut [u8; 10] = (&mut x[0..10]).try_into().unwrap();

The problem is that the compiler is picking the implementation that returns a shared reference and then complains.

6 Likes

Because you're conflating value-level thinking and type-level thinking.

To the type system, 8..40 is just a Range<usize>, so it has no idea how long it is. And thus the type you get out is a slice, not an array.

You'll see this in lots of places in Rust. For example, this doesn't compile:

let x;
if true {
    x = 4;
}
dbg!(x);

Because it doesn't look at the value true, just the type bool.

7 Likes

I am not sure if we are discussing the same thing, but I definitely am misunderstanding something here. Is the issue: there are lots of types:

[u8; 1], [u8; 2], [u8; 3], ..., [u8; N] for arbitrarily large N

and a separate type [u8]

and here, I am confusing a [u8] of length 32 with a [u8; 32] ?

Yes, that's basically the case. In particular, there's only one type that represents 𝓍..𝓎, regardless of the values of 𝓍 and 𝓎: Range<usize>.

Because this doesn't contain any type-level length information, the index operation can't return a type with an embedded length, such as [u8;0], [u8;1], [u8;2], etc. Instead, it uses [u8] which requires the length to be checked at runtime (or at least inside the optimizer).

1 Like