Feature Request: allow partial move of const arrays

I was thinking how const array slices could be concated together (See playground), and I found it possible only to merge whole const arrays. But I'd also, as an owner of a [T;N] would like to get an owned "slice" [T;M] where 1 <= M <= N and throw away all the N-M array positions that I don't care about. I believe this could allow the use case of concating arbitrary slices of const arrays.

So the following should be valid:

let x: [u32; 3] = [1,2,3];
let y: [u32;2] = x[0..=1];

And x becomes a partially moved value.

Currently there is an error (first that y's type is invalid) and second that y doesn't have a size known at compile time!!! Which is not true! We know y's length and y's inner type, and we become the owners of the array.

Safety

If T is not Copy, then of course the following should be invalid and throw an error at compile time (thanks @SkiFire13):

let x: [String; 4] = ["hello".into(), "there".into(), "General".into(), "Kenobi".into()];
let y: [String; 2] = x[0..=1];
x[0]; x[1];

While allowing x[2] and x[3].

A couple of observations:

  • How would this play with const generics? e.g. if x: [T; N] and I do x[0..=1], does it compile? If it does, I guess it will have to panic at runtime if N < 2. If it doesn't, how do we express the contraint N >= 2? Do we have to wait for full const generics and bounds support?
  • Should it work only with literals or also consts? What about calls to const fns with const/literals parameters? AFAIK we currently don't allow calls to const fns in patterns due to possible confusion, so it is blocked on const blocks and patterns.
  • This could be pretty confusing for beginners which start with defining arrays on the stack and can slice them, but the moment they start using slices/vecs/non-literal indexes they, but I guess this is nothing that can't be solved with better diagnostics.
  • This would mean range indexing would become a compiler intrinsic (right now it's implemented with traits, and only normal usize indexing is an intrinsic).
  • A positive side-effect is that this would allow slicing arrays in const fns (since currently it is implemented as a trait it is not available in const contexts)

Note that since u32 is Copy this code would be valid. If it was String or some other non-Copy type then you would be right that it shouldn't compile.

1 Like

This probably is a wild idea, but I would assume that you could write a where N>2 clause (or even better, it would be autogenerated) next to the definition of the const generic, and then on instantiation of N you would get a compiler error?

The rest goes over my head, sorry.

you can already (on nightly) kind of express N > 2

#![feature(const_generics)]
#![feature(const_evaluatable_checked)]

fn foo<T, const N: usize>(src: &[T; N]) -> &T 
where
    [(); N - 2]: Sized,
{
    unsafe { src.get_unchecked(1) }
}

fn main() {
    let arr = [0, 2, 3, 4];

    assert_eq!(&2, foo(&[0, 2, 4, 6]));
    // assert_eq!(&2, foo(&[1])); // compile error!
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=ec299e1c38243a902f90035e5ceb37f9

Now that I think about it, another option would be using patterns. Currently you can already do:

let x: [String; 4] = ["hello".into(), "there".into(), "General".into(), "Kenobi".into()];
let [y @ .., x2, x3] = x;

However the downsides are that you can only have one subarray and you need to have a pattern (either a name or _) for all the other elements.

2 Likes

However the downsides are that you can only have one subarray and you need to have a pattern (either a name or _ ) for all the other elements.

Yeah so it only works if you know the number of elements beforehand. But still cool.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.