dtolnay / no-panic

Attribute macro to require that the compiler prove a function can't ever panic

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is there a way to use potentially panicking code in a #[no_panic] function

KizzyCode opened this issue · comments

While this sounds contra-intuitive, there are std/core-library functions where I can/have to assume that they are correct and won't panic (e.g. str::from_utf8). However since the compiler is unable to prove for some functions that they are panic-free, I cannot use them.

Is there a way to "trust" such a function that it will never panic? Since due to the huge amounts of unsafe code in std/core, I have to rely on their correctness anyway; so it would make no difference for my crate's correctness assumptions…

Ah, I found a way:

#[inline(never)]
unsafe extern "C" fn is_utf8(ptr: *const u8, len: usize) -> bool {
    let slice = slice::from_raw_parts(ptr, len);
    str::from_utf8(slice).is_ok()
}

fn from_utf8(data: &[u8]) -> Result<&str, ()> {
    match unsafe{ is_utf8(data.as_ptr(), data.len()) } {
        true => Ok(unsafe{ str::from_utf8_unchecked(s) }),
        false => Err(()),
    }
}

This is ugly as hell and uses unsafe, but it allows me to use the function without giving up on #[no_panic]. If you know any better way, please let me know; if not please feel free to close this issue 😊

And thank you for no-panic!!

I don't know a better way, but I've added your trick to the readme. Thanks!

For future readers: if you don't use no_std, you can also catch any panic in your unsafe extern "C" function – and you can use Rust-slices as arguments which also makes unsafe unnecessary 🙈.

This means, in std the code above could be replaced with

/// Checks if a string is UTF-8
#[inline(never)]
extern "C" fn is_utf8(slice: &[u8]) -> bool {
    std::panic::catch_unwind(|| str::from_utf8(slice).is_ok()).unwrap_or(false)
}

which is neither unsafe nor does it allow a panic to bubble across FFI-boundaries and invoke UB.