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.