Function core::hint::unreachable_unchecked

1.27.0 (const: 1.57.0) · source ·
pub const unsafe fn unreachable_unchecked() -> !
Expand description

通知编译器调用此函数的站点不可访问,可能会启用进一步优化。

Safety

达到这个函数是 未定义的行为

由于编译器假定所有形式的未定义行为永远不会发生,它将消除周围代码中的所有分支,它可以确定总是会导致调用 unreachable_unchecked()

如果使用这个函数的假设被证明是错误的 – 也就是说,如果调用 unreachable_unchecked() 的站点在运行时实际上是可访问的 – 编译器可能已经为这种情况生成了无意义的机器指令,包括看似不相关的代码,导致难以 - 调试问题。

谨慎使用此功能。 考虑使用 unreachable! 宏,这可能会阻止一些优化,但如果在运行时实际达到它会安全地 panic。对您的代码进行基准测试,以确定使用 unreachable_unchecked() 是否具有性能优势。

Examples

unreachable_unchecked() 可用于编译器无法证明先前建立的不,变体,的情况。如果编译器无法分析的外部代码支持这些不,变体,则这种情况发生的可能性更高。

fn prepare_inputs(divisors: &mut Vec<u32>) {
    // 更改时,注意 future-self: 这里建立的不变量没有在 `do_computation()` 中检查; 如果这种情况发生变化,您必须更改 `do_computation()`。
    divisors.retain(|divisor| *divisor != 0)
}

/// # Safety
/// `divisor` 的所有元素都必须非零。
unsafe fn do_computation(i: u32, divisors: &[u32]) -> u32 {
    divisors.iter().fold(i, |acc, divisor| {
        // 让编译器相信这里不会发生被零除,并且下面不需要检查。
        if *divisor == 0 {
            // 安全性: 由于 `prepare_inputs`,`divisor` 不能为零,但编译器不知道这一点。
            // 我们 *承诺* 我们总是调用 `prepare_inputs`。
            std::hint::unreachable_unchecked()
        }
        // 编译器通常会在此处引入一个检查,以防止被零除。
        // 但是,如果 `divisor` 为零,则上面的分支将到达我们明确标记为不可达的地方。
        // 编译器得出结论,此时 `divisor` 不能为零,并删除 - 现在证明无用 - 检查。
        acc / divisor
    })
}

let mut divisors = vec![2, 0, 4];
prepare_inputs(&mut divisors);
let result = unsafe {
    // 安全性: prepare_inputs() 保证除数不为零
    do_computation(100, &divisors)
};
assert_eq!(result, 12);
Run

虽然在以下示例中使用 unreachable_unchecked() 是完美的,因为编译器能够证明除以零是不可能的,但基准测试显示 unreachable_unchecked() 与使用 unreachable! 相比没有任何好处,而后者不会引入未定义行为的可能性。

fn div_1(a: u32, b: u32) -> u32 {
    use std::hint::unreachable_unchecked;

    // `b.saturating_add(1)` 始终为正 (非零),因此 `checked_div` 永远不会返回 `None`。
    // 因此,else 分支不可访问。
    a.checked_div(b.saturating_add(1))
        .unwrap_or_else(|| unsafe { unreachable_unchecked() })
}

assert_eq!(div_1(7, 0), 7);
assert_eq!(div_1(9, 1), 4);
assert_eq!(div_1(11, u32::MAX), 0);
Run