&Vec<i32> vs &[i32] vs [i32]

I am having difficulty understanding why the following two code examples are different. First the one that works:

fn main() {
    let v: Vec<i32> = vec![1, 2, 3, 4, 5, 6];
    let t = &v[2..4];
    for x in t {
        println!("{}", x);
    }
}

Secondly, the one that does not compile:

fn main() {
    let v: Vec<i32> = vec![1, 2, 3, 4, 5, 6];
    let s = &v;
    let t = s[2..4];
    for x in t {
        println!("{}", x);
    }
}
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
 --> src/main.rs:4:9
  |
4 |     let t = s[2..4];
  |         ^ doesn't have a size known at compile-time

in the second example, s is already an &Vec<i32>. is it not the same as the first example, but with &v divided into two lines instead of one?

the second example is alternatively fixed by adding an additional & to s, like below:

fn main() {
    let v: Vec<i32> = vec![1, 2, 3, 4, 5, 6];
    let s = &v;
    let t = &s[2..4];
    for x in t {
        println!("{}", x);
    }
}

is the issue that in the second example, in the right-hand expression, s is automatically de-referenced before the slice is created and so it evaluates to [i32] not &[i32]? can anyone explain why the second example is different than the first? maybe spreading something out over two lines doesn't guarantee it evaluates to the same type, because its a different order of operations?

playground

When you omit the & you no longer have a very usable slice. The expression s[2..4] alone represents a dynamically sized type (DST) since the length of the range is variable. You need the & or &mut to make it into a fixed size reference to a slice. A slice reference can be copied, passed as a parameter, allocated on the stack, etc.

It can be confusing because sometimes the term slice is used for the DST, but usually it means the slice reference.

Yes, this is a precedence problem. Your first example does this (note the parentheses):

let v: Vec<i32> = /* ... */
let t = &(v[2..4]);

t is set to a reference to v[2..4], which is the type &[i32].

The second example does this:

let v: Vec<i32> = /* ... */
let t = (&v)[2..4];

t is set to the value of (&v)[2..4], which is the type [i32], which is unsized.

&v is not executed in the first example at all. The first example does &(v[2..4]), while the second does (&v)[2..4].

You misunderstand the precedence here. A simple rule suffices to remember this and similar case: in Rust, all postfix operators have higher precedence than all prefix operators. Therefore, &v[2..4] is not equivalent to (&v)[2..4], but to &(v[2..4]). Therefore, if you want to split let t = &v[2..4] into two lines, the accurate way to do so is not

    let s = &v;
    let t = s[2..4];

but rather

    let s = v[2..4];
    let t = &s;

@jumpnbrownweasel already explained one reason this doesn't compile — you can't put a dynamically-sized value on the stack. But another scenario with the same operators wouldn't compile either, for a different but related reason:

    let v: Vec<String> = vec![String::new(), String::new()];
    let s = v[0];

This too would fail, even though s is of the perfectly good type String, because it is trying to move the String out of the Vec<String>, leaving it with an unfilled gap, which is not allowed. The theme in both these cases is that v[0] and v[2..4] refers to a place in memory — a portion of the contents of the Vec. When you take a reference to a place, that is different than writing the place expression by itself, which moves or copies out of the place (or fails to compile if that is not possible).

So, there is a sense in which &(expr)[] is acting as a single operator — you can't separate its parts and get the same result.

thank you, all three of these comments helped me to understand the issue.

in particular, grateful to learn about "precedence" and that seems like it will give me a clear language for thinking about and resolving future issues. In particular all postfix operators have higher precedence than all prefix operators.

The second example of @kpreid also is helping me see how splitting &(expr)[] into multiple lines could cause an effect (a move) that wouldn't happen if it was just one line.

thanks for the clarity!