在上一篇文章使用Rust + Electron開發跨平臺桌面應用 ( 一 )中,咱們將Rust + Electron結合起來,使用Rust編寫核心業務邏輯,並編譯成node庫提供給Electron的UI界面調用,可是在上一篇文章中發現遇到了不少問題,尤爲是Electron 的版本和 Rust編譯出來的版本必需要一致,不然會沒法調用成功,這就很坑了,因此爲了改變這一狀況,今天咱們將使用另外一種方式將Rust的代碼提供給Js進行調用,這就是FFI。node
FFI(Foreign Function Interface)是用來與其它語言交互的接口,因爲現實中不少程序是由不一樣編程語言寫的,必然會涉及到跨語言調用,這時通常有兩種解決方案:c++
一、將函數作成一個服務,經過進程間通訊(IPC)或網絡協議通訊(RPC, RESTful等);編程
二、直接經過 FFI 調用。segmentfault
前者須要至少兩個獨立的進程才能實現,然後者直接將其它語言的接口內嵌到本語言中,因此調用效率比前者高。api
Rust做爲系統級編程語言,也是對FFI提供了完善的支持。安全
因爲rust支持重載,因此函數名會被編譯器進行混淆,就像c++同樣。所以當你的函數被編譯完畢後,函數名會帶上一串代表函數簽名的字符串。
這樣的函數名爲ffi調用帶來了困難,所以,rust提供了#[no_mangle]屬性爲函數修飾。 對於帶有#[no_mangle]屬性的函數,rust編譯器不會爲它進行函數名混淆, 如:網絡
#[no_mangle] pub extern fn test() {}
下面咱們來編寫一個thread_count.rs,其實跟尋常的rust代碼沒有什麼區別:編程語言
#[no_mangle] pub extern fn threadcount(x: i32) -> i32 { let result: i32 = num_cpus::get() as i32; return result * x; }
rust默認編譯成rust自用的rlib格式庫,要讓rust編譯成動態連接庫或者靜態連接庫,須要顯示指定,一共有三種方式,我這裏採用的是直接在Cargo.Toml文件中指定,以下:函數
[lib] name = "thread_count" crate-type = ["dylib"]
須要注意的是name
,必須符合rust的包結構,可以在src目錄下找到。ui
咱們執行cargo build命令,能夠看到,在/target/debug目錄下生成了咱們須要的文件libthread_count.dylib
那麼咱們要如何在JS中調用rust生成dylib呢?答案就是ffi-napi,咱們使用ffi-napi這個包來在js中調用ffi,話很少說,直接看代碼
let ffi = require('ffi-napi'); let path = require('path'); let threadCount = ffi.Library(path.join(__dirname, './target/debug/libthread_count'), { threadcount: ['int', ['int']] }); let result = threadCount.threadcount(12); console.log("thead_count: " + result);
結果以下:
好了,到此爲止,咱們就成功的將rust編譯成動態連接庫給JS調用了,這種方式是我以爲比較好的一種方式,雖然引入函數的方式比較醜,可是咱們不用擔憂node版本的問題。
雖然FFI是一種我認爲比較好的方式,可是它也不是天衣無縫的,例如,在跨越FFI的過程當中,咱們會丟失rust的類型信息,從而引起安全性問題,固然這也不是沒有解決辦法,咱們可使用rust的Box來包裝咱們的類型,這個能夠單獨開一篇文章來說述,就不展開了(先挖個坑,哪天想起來再填)