4
votes

I have a weird looking piece of code and I understand that Rust compiler rejects it, but I don't understand the specific error message.

TL;DR; Why does Rust rejects this with "doesn't have a size known at compile-time" instead of something like "illegal syntax" or "can't assign a slice to a slice"?

fn main() {
    let mut data1 = vec![0, 1, 2, 3].as_slice();
    let mut data2 = vec![8, 9].as_slice();
    data1[1..3] = *data2; // of course this is illegal; but I don't understand the error message
}

This is the code. In theory it should replace a sub slice of data1 with the data in slice data2. (The proper way would be a for loop for example, I know!). But let's have a look at this. Rust compiler says:

error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time
 --> src\main.rs:4:5
  |
4 |     data1[1..3] = *data2;
  |     ^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `[{integer}]`

Why is the error at data1[1..3], only on the left hand side of the assignment? I expected that Rust compiler tells the error is on the right side of the assignment or even the whole assigment. Something like "can't assign a slice to a slice".

But why is Rust telling exactly this message? Why is data1[1..3] of unknown size in this case? Of course [{integer}] is not Sized. But there should be no stack allocation necessary at this point? I'd expect any other error message.

1

1 Answers

5
votes

I can't see a slice on the left hand side of your assignment and neither can the compiler!

Always try to reduce your example as much as possible, most of the time doing so you would find what the compiler is actually complaining about. So, if you would try and write this:

let data1 = [0u8, 1, 2, 3];
let x = data1[1..3];

You would see, what the compiler is actually complaining about in your example:

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
 --> src/main.rs:4:9
  |
4 |     let x = data1[1..3];
  |         ^   ----------- help: consider borrowing here: `&data1[1..3]`
  |         |
  |         doesn't have a size known at compile-time

You see, there's a huge difference between [T] and &[T]! [T] is a contiguous sequence of Ts, while &[T] is a dynamically-sized view into this contiguous sequence. The former has no statically known size while the latter does.

And before you say that you used the Vec::as_slice method, after that you tried to take a slice of a slice, that is:

// Type of `data1` is `&[u8]`
let data1 = vec![0u8, 1, 2, 3].as_slice();

// Type of `x` is `[u8]`
// (which doesn't have a size known at compile-time
let x = data1[1..3];  

So I believe the answer to your question is that the compiler didn't get to the point where it can actually look at the other side of the assignment, because while it tried to figure out the left hand side it already found a problem: an expression that has no known size at compile time.

Now, if you would actually write a slice on the left hand side:

let mut data1 = [0u8, 1, 2, 3];
let data2 = [8u8, 9];

&mut data1[1..3] = &data2[..];

Then the compiler would complain about the invalid nature of the left hand side (among other things):

error[E0070]: invalid left-hand side of assignment
 --> src/main.rs:6:22
  |
6 |     &mut data1[1..3] = &data2[..];
  |     ---------------- ^
  |     |
  |     cannot assign to this expression