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

相關文章
相關標籤/搜索