30

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

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
mhristache
  • 1,768
  • 3
  • 16
  • 18
  • 2
    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 Jul 22 '14 at 22:04
  • 1
    Yeah but compiler has to see the expression is a constant literal, so that could be checked during compilation. – Dmitry Belyaev Jul 22 '14 at 22:58
  • 1
    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 Jul 22 '14 at 23:28
  • 1
    I would guess that the compiler simply does not know what a `Vec` 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 Jul 24 '14 at 19:42
  • 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 Dec 06 '19 at 00:19
  • I'll point out that this *does* fail to compile if `a` is an array. `let a = [1, 2, 3];` results in [*"this operation will panic at runtime"*](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=11923d41d8565e499f2870fd40ee1222). – kmdreko Mar 18 '21 at 04:39

3 Answers3

23

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).

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
Matthieu M.
  • 268,556
  • 42
  • 412
  • 681
  • 4
    llvm, rustc's optimizing compiler backend, does however see through these layers of abstraction when it gets down to optimizing the code. So we can see that it does statically compile it down to a failing bounds check in this particular case. – bluss Feb 08 '15 at 12:00
22

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
bramp
  • 9,203
  • 5
  • 39
  • 45
mwhittaker
  • 1,593
  • 13
  • 18
-10

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.

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
anztrax
  • 725
  • 7
  • 9
  • 6
    This answer is so wrong, there is literally nothing correct about it. – trent Oct 01 '18 at 14:31
  • 4
    Specifically... **1** `a[4]` does not return an `Option` (that's [`slice::get`](https://doc.rust-lang.org/std/primitive.slice.html#method.get)). **2** both accessing by reference or by value will cause the out-of-bounds panic. **3**. [`println!` always borrows its arguments anyway](https://stackoverflow.com/q/30450399/155423). – Shepmaster Oct 01 '18 at 14:52
  • 2
    i see, sorry about that guys, thanks for correcting me :bow: – anztrax Oct 03 '18 at 04:18