Result<T, E>)和不可恢复错误(panic!)提供了 Rust 特色的错误处理机制。int值,其中0表示成功,而负值表示错误。基于setjmp/longjmp的错误处理不属于此节的讨论范畴,如果有必要后面再做说明。T: Sized的Option<&T>和Option<&mut T>,代替显式地进行无效性(nullity )检查的指针。这是由于 Rust 保证了可空指针优化(nullable pointer optimization),在 C 端可以接受可空指针。C 端的NULL在 Rust 中被转换为None,而非空指针被封装在Some中。Result <T,E>是用于返回和传播错误的类型,其实质是一个枚举,其中Ok(T)表示成功并包含一个值,而Err(E)表示错误并包含一个错误值。0 表示正确,-1 表示错误。errno,创建一个线程局部变量(thread_local!),并在每次收到Option参数后进行检查,返回相应的错误信息。std::ptr::null和std::ptr::null_mut来创建表示 C 端的空指针。#[no_mangle]
pub unsafe extern "C" fn handle_option(x: c_float, y: c_float) -> i32 {
// The return value of the function is an option
let result = divide(x, y);
// Pattern match to retrieve the value
match result {
// The division was valid
Some(_) => 0,
// The division was invalid
None => -1,
}
}
#[no_mangle]
pub unsafe extern "C" fn handle_result(s: *const c_char) -> i32 {
if (s as *mut c_void).is_null() {
return -1;
}
let vb = CStr::from_ptr(s).to_str().unwrap();
let version = parse_version(vb);
match version {
Ok(_) => 0,
Err(_) => -1,
}
}
panic会导致未定义的行为(Undefined Behavior,UB),我们还需要确保我们的 FFI 绑定是异常安全(Exception Safety)的。也就是说如果 Rust 导出库的代码可能会出现panic,则需要有个处理机制。在 FFI 绑定时我们可以使用catch_unwind将其包含在 Rust 中,从而不跨越 FFI 边界。use std::panic::catch_unwind;
fn may_panic() {
if rand::random() {
panic!("panic happens");
}
}
#[no_mangle]
pub unsafe extern "C" fn no_panic() -> i32 {
let result = catch_unwind(may_panic);
match result {
Ok(_) => 0,
Err(_) => -1,
}
}
catch_unwind只能捕获 Rust 中的展开(unwinding)panic,而不能处理 Rust 中的终止程序(abort)panic。当出现 panic时,Rust 程序默认会开始展开,这意味着 Rust 会回溯栈并清理它遇到的每一个函数的数据,不过这个回溯并清理的过程有很多工作。另一种选择是直接终止,这会不清理数据就退出程序。那么程序所使用的内存需要由操作系统来清理。通过在 Cargo.toml 的[profile]部分增加panic = 'abort',程序在panic时会由展开切换为终止。
原文作者:Rust语言中文社区;如有侵权,请联系管理员删除。