25
votes

Can someone explain why this compiles:

fn main() {
    let a = vec![1, 2, 3];
    println!("{:?}", a[4]);
}

When running it, I got:

thread '' panicked at 'index out of bounds: the len is 3 but the index is 4', ../src/libcollections/vec.rs:1132

3
Probably the same reason that it doesn't throw a compiler error in any other language; the compiler sees 4 as just an expression, which may as well be f(x) that may return some other value.Colonel Thirty Two
Yeah but compiler has to see the expression is a constant literal, so that could be checked during compilation.Dmitry Belyaev
It could be added in the future or as a plugin, but it's an optimization that requires some evaluation at compile time to know how long is the vector and a pass to check calls to many methods that retrieve elements.snf
I would guess that the compiler simply does not know what a Vec<int> is and that if you start with Vec::new() that it's size is 0 and that if you call push on it three times, its size goes to 3 and that the index in the square bracket has to be less than the vector's size. Vec is just a library type. The compiler has no special knowledge about it.sellibitze
There's a difference between a compile-time error, and a program that you can easily prove will crash at run-time. Your program should correctly panic only at run-time, that is what the lines of code you wrote, tell it to do. Maybe someone can add a lint to the compiler to warn you when do this, but like there's really no purpose and people working on the compiler can better spend their time elsewhere.Nicholas Pipitone

3 Answers

22
votes

In order to understand the issue, you have to think about it in terms of what the compiler sees.

Typically, a compiler never reasons about the value of an expression, only about its type. Thus:

  • a is of type Vec<i32>
  • 4 is of an unknown integral type
  • Vec<i32> implements subscripting, so a[4] type checks

Having a compiler reasoning about values is not unknown, and there are various ways to get it.

  • you can allow evaluation of some expression at compile-time (C++ constexpr for example)
  • you can encode value into types (C++ non-type template parameters, using Peano's numbers)
  • you can use dependent typing which bridges the gap between types and values

Rust does not support any of these at this point in time, and while there has been interest for the former two it will certainly not be done before 1.0.

Thus, the values are checked at runtime, and the implementation of Vec correctly bails out (here failing).

19
votes

If you would like to access elements of the Vec with index checking, you can use the Vec as a slice and then use its get method. For example, consider the following code.

fn main() {
    let a = vec![1, 2, 3];
    println!("{:?}", a.get(2));
    println!("{:?}", a.get(4));
}

This outputs:

Some(3)
None
-8
votes

Maybe what you mean is :

fn main() {
    let a = vec![1, 2, 3];
    println!("{:?}", a[4]);
}

This returns an Option so it will return Some or None. Compare this to:

fn main() {
    let a = vec![1, 2, 3];
    println!("{:?}", &a[4]);
}

This accesses by reference so it directly accesses the address and causes the panic in your program.