使用Rust + Electron開發跨平臺桌面應用 ( 二 )

前言

在上一篇文章使用Rust + Electron開發跨平臺桌面應用 ( 一 )中,咱們將Rust + Electron結合起來,使用Rust編寫核心業務邏輯,並編譯成node庫提供給Electron的UI界面調用,可是在上一篇文章中發現遇到了不少問題,尤爲是Electron 的版本和 Rust編譯出來的版本必需要一致,不然會沒法調用成功,這就很坑了,因此爲了改變這一狀況,今天咱們將使用另外一種方式將Rust的代碼提供給Js進行調用,這就是FFI。node

FFI是什麼

FFI(Foreign Function Interface)是用來與其它語言交互的接口,因爲現實中不少程序是由不一樣編程語言寫的,必然會涉及到跨語言調用,這時通常有兩種解決方案:c++

一、將函數作成一個服務,經過進程間通訊(IPC)或網絡協議通訊(RPC, RESTful等);編程

二、直接經過 FFI 調用。segmentfault

前者須要至少兩個獨立的進程才能實現,然後者直接將其它語言的接口內嵌到本語言中,因此調用效率比前者高。api

Rust做爲系統級編程語言,也是對FFI提供了完善的支持。安全

mangle

因爲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

clipboard.png

JS使用rust的動態連接庫

那麼咱們要如何在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);

結果以下:

clipboard.png

好了,到此爲止,咱們就成功的將rust編譯成動態連接庫給JS調用了,這種方式是我以爲比較好的一種方式,雖然引入函數的方式比較醜,可是咱們不用擔憂node版本的問題。

結語

雖然FFI是一種我認爲比較好的方式,可是它也不是天衣無縫的,例如,在跨越FFI的過程當中,咱們會丟失rust的類型信息,從而引起安全性問題,固然這也不是沒有解決辦法,咱們可使用rust的Box來包裝咱們的類型,這個能夠單獨開一篇文章來說述,就不展開了(先挖個坑,哪天想起來再填)

相關文章
相關標籤/搜索