Infer size from `a[x..y]`

let a:[i32;3] = [1,2,3];
let slice_1: [i32] = a[0..2];

It fails while compile.

the size for values of type `[i32]` cannot be known at compilation time

But won't that be reasonable to infer from [x..y]? The size would just be y-x

  1. The length of arrays can indeed be inferred.

    let a = [1, 2, 3];
    
  2. 0..2 is a Range<_>. It's not the same as 0, 1, even within brackets. Thus...

    let b: [Range<i32>; 1] = [0..2];
    

    [0..2] is an array of length 1, not of length 2; it holds a Range<i32>, not i32s.

  3. [T] is not an array. It's a slice. The length is dynamic and not a part of the type, like it is with arrays. Because the length is dynamic, the size is dynamic too. We say the type is unsized, or doesn't implement Sized, or is a dynamically sized type (DST). Rust doesn't support unsized types in locals, which is what the error is about.

    If you want to annotate an array, you can't just elide the length. The closest thing to what you meant is an unstable feature:

    #![feature(generic_arg_infer)]
    fn main() {
        let c: [i32; _] = [0, 1];
    }
    
3 Likes

No wonder it is called VIEW in the doc
Correct me if I'm wrong.
Take string "abcde"

"ab" can be a slice
"abc" can be a slice
"cde" can be a slice
"abcde" can be a slice

So slice is sort of logical concept.

But it can be sure when add & front of it.
Because &slice is composed of a pointer ,like address, and length which determine the size of slice.

So in convention, we call &slice slice, e.g. &str

I think op is asking for something like Range<T, const N: usize>, so 0..2 :: Range<{int}, 2> and a[0..2] :: [i32; 2] but I'm not sure if there's a reasonable way to make that work even with a time machine (eg no compatibility concerns) without making the common case where the range isn't known worse.

if we had a ConstRange<const N: usize> (possibly with new syntax) it could implement SliceIndex with Output = [T; N]

3 Likes

Hmm, that definitely sounds reasonable with separate syntax...

The languages around slices ([T]) and references to slices (&[T]) tends to be loose and ad-hoc, i.e., calling both of those types "a slice". Slices can also exists in other contexts, like a Box<[T]> ("boxed slice"), which I wouldn't personally call a "view" since the T are owned.

I wrote more about it here.

Rust generally tries to stick to a rule that you can change something from a literal to a function parameter and it won't change the type. It's like how while true { … } isn't the same as loop { … }, because it's helpful to be able to put a constant temporarily without it affecting a bunch of other stuff like borrow checking.

Note that https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.first_chunk stabilized in 1.77, so you can now write

let a: [i32; 3] = [1, 2, 3];
let b: &[i32; 2] = a.first_chunk().unwrap();

or similar if you want to get an array.

(There will eventually be better options on arrays, but those need more bounding questions to be resolved since people would like compile-time checking for things like "get a 10-element prefix of a 2-element array", but there's no stable way to say "where N < M" for a function like this.)

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.