No-Panic Guarantee

Compile-Time No-Panic Guarantee

no-panic-badge cat-no-std-badge cat-rust-patterns-badge

In safety-critical systems—automotive braking, medical devices, aerospace—a panic! is a catastrophic failure, not just a crash. Standard operations like array[i] insert a hidden bounds check that panics on out-of-bounds access.

The #[no_panic] attribute macro makes the compiler prove at link time that a function can never reach a panicking code path. If it can't prove it, the build fails.

Panicking patternSafe alternative
slice[i]slice.get(i) with exhaustive match
for i in 0..len { slice[i] }for &v in slice (iterator)
value.unwrap()match / if let / ?
a / b (integer, b might be 0)Check b before dividing

The example below shows three panic-free functions—aggregation, lookup, and sensor normalization—each proven at compile time by #[no_panic].

use no_panic::no_panic;

/// Sums a slice without any operation that could panic.
///
/// In safety-critical code, a `panic!` is a catastrophic failure.
/// Standard indexing like `slice[i]` inserts a bounds check that
/// calls `panic!` on out-of-bounds access.  Iterators avoid this
/// entirely—the compiler can prove no panic path exists.
#[no_panic]
fn safe_sum(values: &[i32]) -> i64 {
    let mut total: i64 = 0;
    for &v in values {
        total += v as i64;
    }
    total
}

/// Looks up a value by index, returning `None` instead of panicking.
///
/// Using `get()` + exhaustive pattern matching guarantees every code
/// path is handled.  The `#[no_panic]` attribute makes the compiler
/// **prove** it at link time—if any hidden panic path remains, the
/// build fails.
#[no_panic]
fn safe_lookup(data: &[u8], index: usize) -> Option<u8> {
    match data.get(index) {
        Some(&val) => Some(val),
        None => None,
    }
}

/// Clamps a sensor reading into a valid range without panicking.
///
/// Real-world example: an ADC returns a raw `u16` that must be
/// mapped to 0–100 %.  Using `clamp` and simple arithmetic keeps
/// the function panic-free.
#[no_panic]
fn normalize_sensor(raw: u16, min: u16, max: u16) -> f32 {
    if max == min {
        return 0.0;
    }
    let clamped = raw.clamp(min, max);
    (clamped - min) as f32 / (max - min) as f32
}

fn main() {
    let readings = [10, 20, 30, 40, 50];

    let total = safe_sum(&readings);
    println!("sum = {total}");
    assert_eq!(total, 150);

    let val = safe_lookup(&[0xAA, 0xBB, 0xCC], 1);
    println!("lookup index 1 = {val:?}");
    assert_eq!(val, Some(0xBB));

    let miss = safe_lookup(&[0xAA, 0xBB, 0xCC], 99);
    println!("lookup index 99 = {miss:?}");
    assert_eq!(miss, None);

    let pct = normalize_sensor(2048, 0, 4095);
    println!("sensor = {pct:.2}%");
    assert!((pct - 0.5).abs() < 0.01);
}