由于在一般情况下,在编译时检查索引是不可行的。即使对于小程序,推理任意变量的可能值也在困难和不可能之间。没有人想要:
... 对于每个单独的切片/ Vec /等访问,因为这就是您必须执行的操作,以在编译时执行边界检查。您基本上需要依赖类型。
除了可能使类型检查变得不可决定(并使程序的类型检查大大变得更难),通常情况下无法进行类型推断(在最佳情况下受到更严格限制),类型变得更加复杂和啰嗦,并且语言的复杂度显着增加。只有在非常简单的情况下才能很容易地证明索引处于边界内。
此外,几乎没有动力消除边界检查。生命周期通过几乎完全消除垃圾收集的需要来发挥作用 - 垃圾收集是具有不可预测的吞吐量,空间和延迟影响的巨大而具有侵入性的功能。另一方面,运行时边界检查非常无侵入性,具有小而众所周知的开销,并且即使整个程序的其余部分大量使用它,也可以在性能关键部分选择性地关闭它。
请注意,编译器可以对数组的越界访问进行一些简单的检查:
let a = [1, 2];
let element = a[100];
error: index out of bounds: the len is 2 but the index is 100
--> src/main.rs:3:19
|
3 | let element = a[100];
| ^^^^^^
|
= note: #[deny(const_err)] on by default
然而,这种限制可以通过将索引值设为非“明显”的常量来轻松避免:
let a = [1, 2];
let idx = 100;
let element = a[idx];
fn main() {
let a = [1,2,3,4,5];
let index = 10;
let element = a[index];
}
fn main() {
let a = [1,2,3,4,5];
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Failed to read line");
let guess: u32 = guess.trim().parse().expect("Please type a number!");
let element = a[guess];
}
数组访问的索引来自用户输入。编译器无法知道数组将被访问的索引。在这里了解索引的唯一方法是运行程序。这就是为什么Rust在运行时而不是编译时处理数组索引越界错误的原因。