No-Panic Guarantee
Compile-Time No-Panic Guarantee
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 pattern | Safe 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);
}