340 words, 2 min read

Elixir is great for many things, but sometimes you need raw speed or access to native libraries. That’s where Native Implemented Functions (NIFs) come in. This post walks through creating a simple Elixir NIF using Rust with the help of the rustler crate.

Set up your Elixir project

Start with a basic Elixir application:

mix new my_nif_app
cd my_nif_app

Add :rustler as a dependency in mix.exs:

defp deps do
  [
    {:rustler, "~> 0.38.0"}
  ]
end

Fetch the dependencies:

mix deps.get

Generate the Rust NIF scaffold

$ mix rustler.new
This is the name of the Elixir module the NIF module will be registered to.
Module name > MyNif
This is the name used for the generated Rust crate. The default is most likely fine.
Library name (mynif) > my_nif
Fetched latest rustler crate version: 0.39.0
* creating native/my_nif/README.md
* creating native/my_nif/Cargo.toml
* creating native/my_nif/src/lib.rs
* creating Cargo.toml
Updating .gitignore file

Ready to go! See native/my_nif/README.md for further instructions.

This command will:

  • Create a native/ directory
  • Scaffold a Rust crate
  • Link it into your Elixir project

Choose a crate name when prompted, such as my_nif.

Write a simple NIF in Rust

Open the generated native/my_nif/src/lib.rs file and replace its contents with:

#[rustler::nif]
fn add(a: i64, b: i64) -> i64 {
    a + b
}

rustler::init!("Elixir.MyNif");

This defines a basic add/2 function exposed to Elixir.

Define the Elixir wrapper

In lib/my_nif.ex, wire up the NIF:

defmodule MyNif do
  use Rustler, otp_app: :my_nif_app, crate: "my_nif"

  # Fallback when the NIF is not loaded
  def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded)
end

Build and test

Compile the NIF:

mix compile

Then try it out in iex:

$ iex -S mix
iex> MyNif.add(2, 3)
5

You've created a simple Elixir NIF using Rust. This lets you use Rust's performance and ecosystem while retaining Elixir's productivity.