#nif #erlang #ffi

ruster_unsafe

Create Erlang NIF modules in Rust using the C NIF API

4 releases (breaking)

Uses old Rust 2015

0.4.0 Mar 20, 2016
0.3.0 Jan 21, 2016
0.2.0 Aug 18, 2015
0.1.0 Apr 22, 2015

#10 in #nif


Used in ruster

MIT/Apache

36KB
448 lines

Ruster (unsafe)

A crate for creating Erlang NIF modules in Rust. This crate exposes the raw C NIF API which can be used directly or as a foundation for higher layer interface crates. Supported under Unix and Windows.

See the crate documention.

See ruster_unsafe_demo.

Thanks go to Radosław Szymczyszyn for bootstrapping me on this Rust FFI adventure and providing the original automatic bindings.


lib.rs:

Low level Rust bindings to the Erlang NIF API.

NIF Crate

A NIF module is built by creating a new crate that uses ruster_unsafe as a dependency. (more)

NIF Functions

All NIF functions must have the following signature:

extern crate ruster_unsafe;
use ruster_unsafe::*;
extern "C" fn my_nif(env: *mut ErlNifEnv,
argc: c_int,
args: *const ERL_NIF_TERM) -> ERL_NIF_TERM {
// ...
}

NIF Module Initialization

For the Impatient

#[macro_use]
extern crate ruster_unsafe;
use ruster_unsafe::*;

nif_init!(b"my_nif_module\0", Some(load), None, None, None,
nif!(b"my_nif_fun1\0", 1, my_nif_fun1),
nif!(b"my_dirty_fun2\0", 1, my_dirty_fun2, ERL_NIF_DIRTY_JOB_CPU_BOUND)
);

Details

The ruster_unsafe analog of ERL_NIF_INIT() is nif_init! which has the following form:

nif_init!(module_name, load, reload, upgrade, unload, niffunc0, niffunc1, ...)

module_name must be a null-terminated byte array, for example b"mynifmodule\0".

load, reload, upgrade, and unload are optional functions. See load, reload, upgrade, and unload in the Erlang docs. Stub implementations in Rust are:

extern "C" fn load(env: *mut ErlNifEnv,
priv_data: *mut *mut c_void,
load_info: ERL_NIF_TERM)-> c_int { 0 }

extern "C" fn reload(env: *mut ErlNifEnv,
priv_data: *mut *mut c_void,
load_info: ERL_NIF_TERM) -> c_int { 0 }

extern "C" fn upgrade(env: *mut ErlNifEnv,
priv_data: *mut *mut c_void,
old_priv_data: *mut *mut c_void,
load_info: ERL_NIF_TERM) -> c_int { 0 }

extern "C" fn unload(env: *mut ErlNifEnv,
priv_data: *mut c_void) {}

nif! declares NIF functions inside nif_init!:

nif!(nif_name, arity, nif_func, flags)

nif_name must be a null-terminated byte array, for example b"my_nif_fun1\0". arity is the number of parameters accepted by the function. nif_func is the Rust implementation of the NIF. flags is optional and allows you to specify if this NIF is to run on a dirty scheduler. See dirty NIFs in the Erlang docs.

Invoking NIF API

As with any Rust FFI call, NIF API calls must be wrapped in unsafe blocks. Below is an example of invoking NIF APIs along with an approach for dealing with the the args parameter.

extern crate ruster_unsafe;
use ruster_unsafe::*;
use std::mem::uninitialized;
extern "C" fn native_add(env: *mut ErlNifEnv,
argc: c_int,
args: *const ERL_NIF_TERM) -> ERL_NIF_TERM {
unsafe {
let mut a:c_int = uninitialized();
let mut b:c_int = uninitialized();
if argc == 2 &&
0 != enif_get_int(env, *args, &mut a) && 
0 != enif_get_int(env, *args.offset(1), &mut b) {
enif_make_int(env, a+b)
}
else {
enif_make_badarg(env)
}
}
}

Examples

For a complete example see (ruster_demo)

Notes and Limitations

  • Tested on Linux, but any unix should be fine.
  • Windows support is planned but not currently implemented.
  • The NIF threading API is not implemented since Rust provides its own excellent, portable threading API. But this could be implemented if there is a need.
  • Varargs NIF functions are not implemented.
  • ruster_unsafe is based on work by Radosław Szymczyszyn (https://github.com/lavrin/erlang-rust-nif)

Dependencies

~43KB