erlang的nif通常是用C写,然后编译成动态库,在erlang里面通过erlang:load_nif加载动态链接库,在看rust时候发现rust也能变成动态库给c调用,想了下根据原理,rust应该也可以给erlang写nif,并且rust相比c有内存安全,更方便的工程与依赖库管理编译工具的优势。

查了一下,rust已经有现成的工具库实现了。

  • Rustler, 提供更方便的写法实现rust和erlang之间参数传递,内存安全。rustler和elixir更搭,通过mix,可以轻松组建工程。

  • erlang_nif-sys, 用于写更底层的erlang nif,rust代码里面都会有unsafe包住。

为验证rust写的nif可以在erlang中使用,随便写了例子玩一下。

rust 安装

`curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`

测试环境

  • centos 7.4
  • erlang OTP 20
  • rust 1.43
  • rustler 0.21

example

rust代码,代码相比用C写,要稍微简洁易懂些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#[macro_use]
extern crate rustler;

use rustler::{Encoder, Env, NifResult, Term};
rustler_export_nifs!(
"test_inf",
[("add", 2, add)],
Some(on_load)
);

fn on_load<'a>(_env: Env<'a>, _load_info: Term<'a>) -> bool {
println!("Runs on library load");
true
}

fn add<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
let num1: i32 = args[0].decode()?;
let num2: i32 = args[1].decode()?;
Ok((num1 + num2).encode(env))
}

rust依赖与编译配置

1
2
3
4
5
6
7
8
9
10
[package]
name = "rust_in_erlang"
version = "0.1.0"

[lib]
name = "test_inf"
crate-type = ["dylib"]

[dependencies]
rustler = ">=0.13.0"

erlang代码

1
2
3
4
5
6
7
8
9
10
11
12
-module (test_inf).

-export([add/2]).

-on_load(init/0).


init() ->
ok = erlang:load_nif("target/debug/libtest_inf", none).

add(_X, _Y) ->
exit(nif_library_not_loaded).

整个工程的测试代码已上传github,获取代码

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
> cargo build

Finished dev [unoptimized + debuginfo] target(s) in 0.60s

> erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V9.3 (abort with ^G)
1> c(test_inf).
Runs on library load
{ok,test_inf}
2> test_inf:add(1,2).
3

well well well , it works!