Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

English Original

17.2 避免未检查的索引 🟢

在 Rust 中,如果索引超出范围或键不存在,对 VecHashMap 等集合使用索引运算符 ([]) 将导致程序发生 Panic。这是 Rust 程序中常见的崩溃来源。

1. [] 的危险性

虽然 [] 很方便,但它假设索引或键始终有效。如果这个假设错误,你的程序就会崩溃。

fn main() {
    let v = vec![1, 2, 3];
    
    // 不良实践:这会发生 Panic,因为索引 10 超出了范围
    let x = v[10]; 
}

2. 使用 .get() 代替

.get() 方法返回一个 Option<&T>,允许你在不崩溃的情况下处理索引或键缺失的情况。

fn main() {
    let v = vec![1, 2, 3];

    // 良好实践:优雅地处理缺失的索引
    match v.get(10) {
        Some(x) => println!("值为:{}", x),
        None => println!("索引超出范围!"),
    }

    // 或者使用 unwrap_or 提供默认值
    let x = v.get(10).unwrap_or(&0);
}

3. 使用迭代器代替索引

通常,你可以通过使用迭代器完全避免索引。这不仅更安全,而且通常更高效、更地道 (idiomatic)。

fn main() {
    let v = vec![1, 2, 3, 4, 5];

    // 不良实践:手动索引很容易出错
    for i in 0..v.len() {
        println!("{}", v[i]);
    }

    // 良好实践:使用迭代器
    for x in &v {
        println!("{}", x);
    }
}

4. 边界检查与切片 (Slicing)

如果你需要处理集合的一个子部分,请使用 .get(start..end) 进行切片。这也会返回一个 Option,确保你不会意外地越过边界。

fn main() {
    let v = vec![1, 2, 3, 4, 5];

    if let Some(sub_slice) = v.get(1..3) {
        println!("子切片:{:?}", sub_slice); // [2, 3]
    }
}

对于 C/C++ 开发者的总结

  • 在 C/C++ 中:对 std::vector 使用 v[i] 不执行边界检查,如果索引超出了范围,会导致未定义行为(读取/写入任意内存)。你可能会使用 v.at(i),它会抛出异常,但这种情况不太常见。
  • 在 Rust 中v[i] 始终执行边界检查并在失败时发生 Panic。这可以防止内存安全问题,但仍可能导致你的程序退出。使用 .get() 是 Rust 处理潜在“越界”访问的安全且显式的地道方式。