otter — write fast and efficient Erlang NIFs in Rust
If you're an Erlang developer trying to write NIFs in Rust, you've probably run
into a few obstacles. The de facto library for writing Rust NIFs, rustler, has
documentation mostly written for Elixir. Its API wraps the native erl_nif API
in ways that block significant capabilities, like NIF upgrades, and obscure
others, like improper lists. You can get a NIF to build, but you're flying
blind, trusting that the abstraction did the right thing underneath without ever
being shown what it did.
otter is an Erlang-first NIF library built on the opposite premise: you
should be able to see exactly what's happening. Every type and function maps
thinly and explicitly onto the erl_nif call beneath it, so you can read
otter's docs next to the erl_nif man pages and know precisely what your NIF
does — what call fires, when it raises, how a term is really shaped. Improper
lists are first-class cons cells; decoder errors name the actual failure; the
full message-send matrix is exposed; and every module is hot-code-upgrade-safe.
use otter::types::CallEnv;
#[otter::nif]
fn add(_env: CallEnv<'_>, a: i64, b: i64) -> i64 {
a + b
}
otter::init!("my_nifs", [add]);→ Full quick start, types, and feature tour, see otter/README.md
→ For a detailed, point-by-point comparison with rustler, see docs/RUSTLER.md.
→ For a complete working example app, see otter-demo.
This is the monorepo for otter (published as otter-nif on crates.io).
For the introduction, quick start, and feature tour:
| Path | Crate / App | Package | Docs | What it is |
|---|---|---|---|---|
otter/ |
otter-nif |
Core Rust library — types, codecs, environment, resources (imported as otter) |
||
otter_codegen/ |
otter-nif-macros |
— | Proc macros (#[otter::nif], otter::init!), re-exported through otter-nif |
|
rebar3_otter/ |
rebar3_otter |
rebar3 plugin — drives cargo build/cargo clean and installs the NIF into priv/ |
||
otter_test/ |
otter_test |
— | — | In-tree Erlang app — end-to-end test suite exercising the bridge |
You only ever depend on otter-nif (imported as otter); the codegen macros are
re-exported through it.
| Document | Contents |
|---|---|
| otter/README.md | Start here — introduction, quick start, feature tour |
| docs/USAGE.md | User-facing guide — setup, all types, atoms, resources, message passing, scheduling, select |
| docs/RESOURCES.md | Deep dive on the resource lifecycle |
| docs/UPGRADE.md | Hot-upgrade safety model and the no-cross-build-ABI invariant |
| docs/RUSTLER.md | Design comparison with rustler |
| docs/MIGRATION.md | Side-by-side rustler-to-otter migration guide |
| otter/DESIGN.md | Core library architecture and internals |
| otter_codegen/DESIGN.md | What the macros generate, argument/return type rules |
| rebar3_otter/DESIGN.md | Plugin providers, cargo integration, NIF loading path |
otter-nif — the Rust library
- OTP 26 (NIF 2.17) runtime floor; the optional
nif_2_18feature requires OTP 29 (NIF 2.18) - Rust edition 2021, MSRV 1.82 (
otter-nif-macros1.56) cargoonPATH
rebar3_otter — the rebar3 plugin
- OTP 27 (it uses OTP-27 doc syntax)
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.