如何将Rust库触发的Panic捕获为C++可捕获的异常?
问题
测试一个会触发Panic的Rust库并通过C调用,但无法在C中捕获Rust产生的Panic。相关代码及运行情况如下:
Rust库配置与代码
Cargo.toml
[package] name = "boom" version = "0.1.0" edition = "2018" [dependencies] libc = "0.2" [lib] crate-type=["staticlib"]
src/lib.rs
#[no_mangle] pub extern "C" fn boom() { panic!("Oh no, something went wrong!"); }
C++初始调用代码
// g++ -g boom.cpp -L./target/debug -lboom -o boom extern "C" { void boom(); } int main() { boom(); }
运行结果:
thread '<unnamed>' panicked at src/lib.rs:3:5: Oh no, something went wrong! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace fatal runtime error: failed to initiate panic, error 5
尝试捕获Panic的C++代码
// g++ -g boom.cpp -L./target/debug -lboom -o boom #include <stdexcept> extern "C" { void boom(); } int main() { try { boom(); } catch(const std::exception& e) { printf("exception captured\n"); } }
运行结果与之前一致,无法捕获Panic。请问是否有方法在C中捕获Rust库产生的Panic?或能否将Rust Panic转换为C可捕获的异常?
解决方案
Rust的Panic与C异常是完全独立的底层机制,**无法直接在C中捕获Rust Panic**。要实现跨语言错误处理,必须在Rust侧做转换处理,常见方案有两种:
方案1:捕获Panic并返回C风格错误码
修改Rust代码,用std::panic::catch_unwind捕获Panic,将其转换为C语言可识别的整数错误码:
修改后的src/lib.rs
use std::panic; use libc::c_int; #[no_mangle] pub extern "C" fn boom() -> c_int { match panic::catch_unwind(|| { panic!("Oh no, something went wrong!"); }) { Ok(_) => 0, // 无错误 Err(_) => 1, // 捕获到Panic,返回错误码 } }
对应的C++调用代码:
// g++ -g boom.cpp -L./target/debug -lboom -o boom #include <stdio.h> extern "C" { int boom(); } int main() { int ret = boom(); if (ret != 0) { printf("Rust Panic captured via error code\n"); } return 0; }
方案2:将Panic转换为C++异常
如果希望用C的try/catch捕获,可在Rust侧捕获Panic后,调用C函数主动抛出异常(注意:该方法依赖编译器的异常实现,可移植性有限):
修改后的src/lib.rs
use std::panic; use libc::{c_char}; use std::ffi::CString; extern "C" { fn throw_cpp_exception(msg: *const c_char); } #[no_mangle] pub extern "C" fn boom() { let result = panic::catch_unwind(|| { panic!("Oh no, something went wrong!"); }); if let Err(panic_msg) = result { // 将Panic消息转换为C字符串 let msg = panic_msg.downcast_ref::<String>() .map(|s| CString::new(s.clone()).unwrap()) .unwrap_or_else(|| CString::new("Unknown panic").unwrap()); // 调用C++函数抛出异常 unsafe { throw_cpp_exception(msg.as_ptr()); } } }
对应的C++代码:
// g++ -g boom.cpp -L./target/debug -lboom -o boom #include <stdio.h> #include <stdexcept> #include <cstring> extern "C" { void boom(); // 供Rust调用的异常抛出函数 void throw_cpp_exception(const char* msg) { throw std::runtime_error(msg ? msg : "Unknown panic"); } } int main() { try { boom(); } catch(const std::exception& e) { printf("Exception captured: %s\n", e.what()); } return 0; }
注意事项
catch_unwind无法捕获所有Panic:例如调用std::process::abort()或栈溢出引发的Panic,这类情况会直接终止程序,无法被捕获。- 跨语言异常的兼容性:方案2依赖C++编译器的异常实现(如GCC的
__cxa_throw),不同平台或编译器可能存在兼容性问题,生产环境优先推荐方案1的错误码方式。
内容的提问来源于stack exchange,提问作者fgalan
相关产品推荐
相关产品推荐

