Why should both the type and the number of elements be the same?

Hello,
I know the following code shows an error, because it is true that the type of elements is the same, but the number of elements is different:

fn main() {
   let mut x=["A","B","C"];
   x=["A","B"];
   println!("{},{},{}",x[0],x[1],x[2]);
}

But, what is the behind this error? Is it just for security?

Thank you.

It's less about security or safety and more about how arrays simply work.

That is not to say, certainly it can also be a memory safety related thing: Having the concept of a "fixed size array" allows function signatures to specify exactly how many elements they want, within the function, bounds checks for constant indices could be optimized away, APIs can be harder to misuse of they're able to enforce array lengths, etc… And once you've established this restriction, of course it must be rigorously enforced for type safety.

However, there's another aspect, and in many Rust programmer's minds, this will be the true reason: It's about how arrays work. Arrays are a very simple datatype in Rust. A value of type, say, [T; 3] consists simply of 3 values of type T stuck together in memory, right next each other. Notably, handling an array does mean, you handle these values directly. No pointers, no heap data, no length information, no overhead. If you have a [T; N] on a local variable, then those N amount of Ts will sit directly on your stack memory, taking up N * mem::size_of::<T>() space, and for something to be allowed to sit on the stack, its size needs to be fixed and determined at compile-time.

There exist other types that are different. If you still want a fixed size limit so things can lay on the stack, but are fine with the overhead of saving the length information at run time, too, to gain flexibility in the actual length, then something like ArrayVec in arrayvec - Rust does that.

If you want no length limitation but in turn are fine with moving the data into the heap, then Vec<T> is the most commonly used type for that. If you have the data behind some level of indirection anyways, then slices are a powerful tool to make length flexible, as well as to abstract over the data in (borrows of) various other data structures including all the ones mentioned before, i. e. arrays, ArrayVec or Vec, and more.

4 Likes

Perhaps you're looking for copy_from_slice (memcpy).

   let mut x = ["A","B","C"];
   x[..2].copy_from_slice(&["D", "E"]);
   println!("{},{},{}",x[0],x[1],x[2]);
2 Likes

The way I look at it is:

  1. x is created as a mutable variable that is initialised to value which is an array containing 3 elements.
  2. x is mutated. Its old value is replaced with a new array that only contains 2 elements.
  3. The println! is then used to print the third element of the new array, which does not exist.

Now, what would you do in this situation:
a) Print some random number that happens to be in the memory space where the third element should be, as C might, if it does not crash the program?
b) Print undefined as Javascript would?
c) Something else that is of no use?

What would you do if your program went on to use that missing element for something important? Just produce the wrong results for you to debug.

By analogy, if you buy a box of 6 eggs for the six people you are making breakfast for you would be a bit angry at finding the box only contained 5 eggs!

My feeling is that this is primarily to push one towards writing good quality code, reducing the chance that ones silly mistakes result in programs that does the wrong thing or crash. It makes you spend time thinking about the correctness of your code rather than spend time debugging it later or have your users facing unexpected failures long after you have shipped your product.

Of course this kind of thing helps create more secure software. Buffer over runs, such as this, are a major source of security vulnerabilities. That is to say "helps", there are plenty of other ways to introduce security vulnerabilities even when ones code builds cleanly.

1 Like

The number of elements is a part of the type for arrays, that's why.

Anyway, if you assign a two-element array to a variable, then it doesn't make any sense to ask for the 3rd element (at index 2) later on, so I'm not sure what you would want this program to even mean.

Did you want to use a slice and only print the first two elements instead?

fn main() {
   let mut x: &[_] = &["A", "B", "C"];
   x = &["A", "B"];
   println!("{}, {}", x[0], x[1]);
}
1 Like

Hello,
Thank you so much for your reply.
I just wanted to know why and it was a test.

Hello,
Thank you so much for your reply.
A and B could replace A and B. I just wanted to know why Rust-Lang banned it.

Hello,
Thank you so much for your reply.
But, the array has enough space. I guess the Rust-Lang banned it because of the security.

Assignment of a complete value is not the same as overwriting a part of a slice. You are conceptually mistaken. This hasn't got much to do with "security".

2 Likes

Interesting. I would naturally assume that after the:

x=["A","B"];

x is no longer the same array. It is a new array with two elements instead of three. However I was a little wrong there because the length of the array is part of the type so that assignment fails as it is given the wrong type. Which is what the build error says:

3 |     x = ["A", "B"];
  |         ^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 2 elements
3 Likes

Hi everyone,
Thanks for all the replies.

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.