前言
近段时间学习了 Rust,一直想着做点什么东西深入学习,因为是刚学习,很多地方都不熟悉,所以也就不能拿它来做编译器这些,至于 web 开发,实际上我并不建议拿这个来学习一门语言,大概有几个方面,一是 web 开发的套路无非也就那么几个,对学习一门语言并不会有多大的帮助。二是 web 开发大多已经被封装了很多东西,对学习语言本身其实不利,真的要深入学习的话还是建议从语言本身出发,尽量不要用封装好的东西,当然,标准库除外。
为什么是 Rust + Electron
原因其实很简单,我不想做太复杂的东西,因为大部分的精力还是要放在工作上,其次是希望做一个我日常能用的东西,当然现在还没想好,可能是个音乐播放器,也可能是个天气展示的 app,这样我就可以每天使用了,这也会更有动力促使我开发好它。
Rust 和 Electron 想必就不用我多介绍了吧,至于为什么是这个组合可以查看知乎的这个问题,我赞同的是的方案是
使用 C/Cpp/Rust 开发的核心 + Electron / Qt 开发界面
本期目标
本期的目标非常简单,将 Rust 和 Electron 结合起来,使用 Rust 获取电脑 cpu 核数,Electron 将数据绘制在界面上展示。
初始化 Electron 项目
Electron 项目的初始化我用的工具是 electron-forge,首先我们按照 electron-forge 的官网介绍来
npm install -g electron-forge
electron-forge init my-new-project
cd my-new-project
electron-forge start
解释一下,首先我们要安装 electron-forge,这是一个脚手架工具,类似于 Vue-cli。然后我们初始化一个项目,项目名称为 my-new-project。
需要注意的是这初始化的过程中 electron-forge 会构建 package.json, 然后下载依赖,我第一次下载依赖的时候卡在了 electron-runtime,第二次重试的时候就好了。
第二个是 electron-forge 中的依赖会对 Python 版本有要求,只能要求 Python2,这里要注意的一点是,我十分不建议使用 pyenv 来控制 Python 版本,会出现以下错误,我的解决方式是使用 virtualenv 新建一个 Python2 的环境。
Fatal Python error: PyThreadState_Get: no current thread
现在我们来看一下项目结构
整个项目结构非常简单,src 中是我们的源文件,index.html 是界面文件,index.js 是界面逻辑文件,大家打开 index.js 就可以看到一段自动生成的代码,主要是创建了一个 app,以及监听 app 的活动,需要注意到的是其中对 mac 的处理。
app.on(‘window-all-closed’, () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== ‘darwin’) {
app.quit();
}
});
好了,现在让我们把项目跑起来,在项目目录下执行 electron-forge start 命令,稍等一会我们就可以看到界面运行起来了
初始化 Rust 项目
在开发之前我们要知道,JS 是无法直接运行 Rust 的,就像 JS 无法直接运行 C ++ 一样。所以我们需要将 Rust 打包成 Node 模块提供给 JS 进行调用。所以我们会使用 neon 来做到这件事,neon 的 github 地址在这里
首先我们需要安装 neon,注意,neon 对 python 版本也是有要求的,如果你是 mac,python 版本必须要是 Python2.7,不支持 Python3,同样,这里也会出现上面说过的 no current thread 问题,所以我们在开发时最好用 virtualenv 新建一个 Python2 的环境。
安装完 neon 之后我们执行 neon new thread-count,新建一个项目。看一下项目结构
lib 是我们最终的导出文件,提供给 electron 进行调用,native 下则是我们的 rust 代码,注意,这里的入口文件是 native/src/lib.rs,因为我们建立的是一个库而不是一个可执行的应用程序。让我们先编译项目,在文件目录下执行 neon build –release 命令。
让我们进入终端调用一下项目试试:
成功!到现在我们就成功的将 rust 写的代码封装成 node 库,使得 JS 可以进行调用了,接下来我们回到上面说过的,将 rust 的功能更改为获取 CPU 核数,然后将它封装成一个函数并进行导出。
首先我们要修改 Cargo.toml, 在 [dependencies] 下增加一个 num_cpus = “1.4.0” 的依赖项,然后修改 native/src/lib.rs 文件如下
#[macro_use]
extern crate neon;
use neon::prelude::*;
fn thread_count(mut cx: FunctionContext) -> JsResult<JsNumber> {
Ok(cx.number(num_cpus::get() as f64))
}
register_module!(mut cx, {
cx.export_function(“thread_count”, thread_count)
});
修改 lib/index.js 如下:
var addon = require(‘../native’);
module.exports = addon.thread_count;
然后我们再进行编译,执行 neon build –release 命令,然后再进入终端调用这个函数试试
成功啦,至此,我们就成功的将 rust 代码封装给 JS 进行了调用。需要注意的是编译 rust 的 node 版本需要与运行 electron 的 node 版本一致,否则会出现无法调用的情况。好了,到此第一期就结束了,代码我会抽空整理到 github,以供有需要的同学查看。
最后看一下效果图吧
ps: 现在 Rust 的各项工具和库都不是很成熟,所以大家再实践过程中会遇到各种问题,都可以评论到下面大家一起讨论。