Why am I not allowed to copy part of a list filled with i32 to another variable?

fn main()
{
    let x = [0, 1, 2, 3, 4];
    let y = &x[0..=2];

    println!("{:?}", y);
}

So if I want to copy between 0 to 2 elements into variable y why am I only allowed to reference it but not copy it like this let y = x[0..=2];?

It sounds like you may want y to have type [u8; 3]. The indexing operator ([]), when used on an array or slice produces a slice. A slice, which has variable length, is a type that exists only as a reference, so you can't create a copy.

You could use pattern matching to create an array from another array:

let a = [0, 1, 2, 3, 4];
let [_, b @ .., _] = a;

There are also crates that can help you convert slices to arrays, see e.g. https://docs.rs/array-tools/0.2.10/array_tools/fn.split.html

let a = [0, 1, 2, 3, 4]; So a is then a reference to this list? Are the individual elements (such as 1, 2, 3 etc) stored on the stack or the heap?


How do you make your code colour coded (for example the integers are light bluish/greenish colour)?

No, it's not. In that piece of code, a is an array because you created it by means of an array literal, not by means of indexing.

By default, values created using let bindings go on the stack, but that's a separate issue. If you want heap allocation, you must explicitly ask for it by using the appropriate container type such as Box or Vec.

This, however, has precisely nothing to do with the fact that a slice obtained by indexing (e.g. a[0..2]) is unsized, and is thus not currently possible to access by value. Even if it weren't unsized, it would be impossible to deduce its size statically, since the indexing operator is allowed to be used with any range at runtime, and as such, it knows nothing statically about the size of its own result.

3 Likes

Sorry mate, what do you mean by unsized?

I mean this.

1 Like

It does look a bit odd to the untrained eye. For example in code like this:

fn main()
{
    let x = [0i32, 1i32, 2i32, 3i32, 4i32];
    let y = x[0..=2];

    println!("{:?}", y);
}

It looks like the size of everything is known at compile time. The elements are i32, we see exactly 5 of them in x and y looks like a fully specified slice that could just be a copy of that fully defined x.

If we can do it as a copy for a scalar i32 and such why not 5 of them?

1 Like

@ZiCog you can certainly copy arrays.

fn main()
{
    let x = [0i32, 1i32, 2i32, 3i32, 4i32];
    let y = x;

    println!("{:?}", y);
}

As I mentioned originally, it's the indexing operation that prevents copying.

1 Like

Yes but it is not obvious to the neophyte why that indexing operation is not full specified and therefore the size of the required copy known at compile time.

It's not as if it said:

for some unknown "z".

1 Like

Take a look at this topic:

1 Like

Thanks I will have a look at it

edit: Oh I see now thanks for showing me this.

The problem is that since an index is essentially a method call to std::ops::Index::index, it really is exactly like that. Rust tends, on a whole, to be very consistent. Calling a method with constant parameters won't get you any special return type, it's going to behave exactly as if you had passed it a variable.

There could be a language which does this analysis, but Rust is not that language. I think overall, this makes it more consistent to work with, as you don't unexpectedly get different types when doing operations with constants vs. with variables.

Doing the analysis to tell that let y = x[0..=2]; is different from let y = x[0..=z] isn't necessarily trivial either, though. I could imagine it working well, but it could also go horribly wrong and be more confusing than not. For instance, this would fix OP's case, but as soon as they create a dynamically sized array, their code stops working. Or if they want to change the start of the bound to something obtained dynamically (or gotten from a function parameter), it suddenly can't work because you could end up with a 0 or 1-sized array. I mean, the language could go fully into Ada-land and annotate integer types with the range of variables they represent, but I think the maintenance burden quickly overwhelms the usefulness if you don't need to use that for safety.

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.