使用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来包装咱们的类型,这个能够单独开一篇文章来说述,就不展开了(先挖个坑,哪天想起来再填)

相关文章
相关标签/搜索