Two very popular C libraries: libjpeg and libpng require unwinding for error handling. Rust doesn’t support interacting with them that way, and for me that’s a huge gap in Rust’s FFI story.
These libraries are specifically designed to be safe and not leak or break anything when their error handler unwinds (it’s plain dumb C, no destructors, and all heap allocations are tracked via libraries’ handles). In C they are most often used with setjmp
/longjmp
, but the APIs are setjmp
-agnostic and work with anything that can unwind C’s stack.
extern "C" {
pub fn c_lib_call();
}
extern "C" fn callback_from_c() { panic!("go back to Rust"); }
fn main() {
catch_unwind(|| c_lib_call());
}
extern callback_from_c() __attribute__((noreturn));
void c_lib_call() {
callback_from_c();
}
Here’s the problem: Rust has paranoid approach to FFI unwinding that is ruined by (non)support for C++ and arbitrary black-box languages (which is understandable and I wouldn’t ask to support them). Unfortunately, that also throws away support for these common unwind-safe C libraries.
Up to Rust 1.23, even though it was theoretically undefined behavior, I was able to configure libpng and libjpeg to panic!()
on error and catch that panic in Rust. This way I was able to use these libraries with pure-Rust wrapper, with no extra C code required. It also was as almost efficient as C, since I was able to set up catch_panic
once for all FFI calls.
But Rust 1.24.0 (is the change coming back in a later version?) is intentionally preventing unwinding through FFI, which blocks the (unintentional) support libjpeg/libpng error handling method. Now I have to create setjmp-based wrappers in C for all my uses of these libraries. I can’t have pure-Rust project that just links to existing libraries on the system. My project has to include extra C code and depend on a C compiler. That’s a step backwards for me.
I know I could be calling FFI function that goes through C++, JVM JNI, custom assembly, and code compiled with exotic ABIs and extra instrumentation — but I also know when I’m definitely not doing any of this. Could Rust have “I know what I’m doing” attribute for unwinding through FFI?
When I’m calling plain-C libraries that are designed for unwinding I wish I could tell Rust not to try to save me from myself:
extern "C" {
#[hey_rust_this_will_unwind]
pub fn jpeg_start_compress(cinfo: &mut jpeg_compress_struct, write_all_tables: jboolean);
}
#[hey_rust_let_me_unwind_this]
extern "C" fn c_lib_error_handler(cinfo: &mut jpeg_common_struct) {
panic!("{}", cinfo.err.msg_code);
}