Rust FFI 编程 - Rust导出共享库04

这节咱们主要关注 Rust 导出共享库时的错误处理。主要涉及到:
  • Option 和 Result 的处理
  • panic 的处理
错误对于软件来讲是不可避免的,错误处理是保证程序健壮性的前提,编程语言通常都会有一些机制来处理出现错误的状况,大体分为两种:抛出异常和做为值返回。
Rust 中没有异常,而是将错误做为值返回,而且经过将错误分红两个主要类别可恢复错误( Result<T, E> )和不可恢复错误( panic! )提供了 Rust 特点的错误处理机制。
C 虽然错误处理机制简陋,但最多见也是将错误做为值返回,其中的 POSIX 风格就是函数返回一个 int 值,其中 0 表示成功,而负值表示错误。基于 setjmp / longjmp 的错误处理不属于此节的讨论范畴,若是有必要后面再作说明。

Option 和 Result 的处理

在 FFI 中容许使用任何 T: Sized Option<&T> Option<&mut T> ,代替显式地进行无效性(nullity )检查的指针。这是因为 Rust 保证了可空指针优化(nullable pointer optimization),在 C 端能够接受可空指针。C 端的 NULL 在 Rust 中被转换为 None ,而非空指针被封装在 Some 中。
咱们知道 Rust 中的 Result <T,E> 是用于返回和传播错误的类型,其实质是一个枚举,其中 Ok(T) 表示成功并包含一个值,而 Err(E) 表示错误并包含一个错误值。
在设计 Rust 导出共享库时,咱们能够使用返回值的错误处理机制,使 C 调用者能够经过检查返回值来检测什么时候发生了错误,并得到相关的错误信息。对于 Option 和 Result 的转换,咱们通常采起如下一些方法:
  • 简单的返回 C 中经常使用的数值, 0  表示正确, -1  表示错误。
  • 返回相似于 C 中的全局  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 的处理

同时跨越 FFI 边界的 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 时会由展开切换为终止。
完整代码:https://github.com/lesterli/rust-practice/tree/master/ffi/example_02
相关文章:
  • https://s3.amazonaws.com/temp.michaelfbryan.com/errors/index.html
  • https://michael-f-bryan.github.io/rust-ffi-guide/errors/index.html
  • https://doc.rust-lang.org/nomicon/repr-rust.html

本文分享自微信公众号 - Rust语言中文社区(rust-china)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。html

相关文章
相关标签/搜索