Rust considers indexing expressions like [0..8] to be entirely runtime-calculated index values. You might as well have used two random values x and y and written self.array[x..y], so the size of this part of the array is completely unknown; hence it’s represented as the slice type [u8] which can only be interacted with in Rust if its kept behind some indirection.
So the expression &self.array[0..8] works fine. (Don’t think of the self.array[0..8] part of this expression as evaluating to it’s own thing, on a first approximation, the &foo[ix] syntax stands for a single step/operation at run-time).
Iterating over &[T] with a for loop works, too, due to its IntoIterator implementation. The items i will be of type &T, in this case &u8. If you’d like to have them by-value, you might as well write for &i in &self.array[0..8] to pattern match the level of reference away.
Analogous operations are possible with mutable references, too, so something like
I tried to explain everything above. Try to ask a more specific question, what part you don’t understand
I assume your question is why the compiler isn’t “smart enough” so see the compile-time-known indices here. The answer is: It’s hard to offer this via the same API without potentially getting some confusing or inconsistent behavior. Whether something is or isn’t “compile-time-known” would presumably be more of a heuristic result than a clear rule, on the other hand the return type ([T] vs [T; N]) would depend on that, and types should be more predictable.
Also, changing it now could lead to breakage as [T] and [T; N] are – though usable interchangably in many contexts – distinct types. The only reasonably action would be to introduce a new syntax or method for constant-indexing arrays. In fact some work in this direction is being done with this or this method, and on stable rust, you can also convert &[T] back to fixed-sized &[T; 8] (or other lengths) with the relevant TryFrom implementation and an unwrap.