This document is intended to simplify upgrading to newer versions by extending the changelog.
rustler_sys
as a standalone library has been replaced by an embedded
rustler::sys
submodule. Due to how the rustler_/::sys
initialisation works,
it is not possible to use the new rustler
in conjunction with rustler_sys
. A
simple textual replacement is enough, though.
- NIF implementations are now discovered automatically, the respective argument
in the
rustler::init!
macro should be removed. If a NIF implementation should not be exported, it must be disabled with a#[cfg]
marker. - The functionality related to the
derive
feature is now unconditionally active. The feature flag is kept for compatibility for now but will be removed in the future. - To use a type as a resource, the
Resource
trait should now be implemented on the type, which also allows for specifying a destructor (taking anEnv
argument) or adown
callback for process monitoring. If the recommendedresource_impl
attribute is used on theimpl
block, the type will by default be automatically registered and theIMPLEMENTS_...
constants will be set for implemented callbacks.
The macro changes that where already carried out in version 0.22
are now
mandatory, the deprecated macros have been removed. Please see below for
documentation on how to convert from the old to the new set of macros.
- The functionality of
rustler_bigint
has moved intorustler
. The library will still work, but it can now also be replaced by activating the newbig_integer
feature onrustler
. The newrustler::BigInt
is a re-export ofnum_bigint::BigInt
in contrast torustler_bigint::BigInt
, which was a wrapper. For most codebases, it will be enough to activate the feature and replace allrustler_bigint::BigInt
usages byrustler::BigInt
(ornum_bigint::BigInt
). serde_rustler
has been integrated intorustler
behind the feature flagserde
. Arbitrary,serde
-compatible objects (i.e. withDeserialize
orSerialize
impl
s) can be wrapped inSerdeTerm
to use them in place ofEncoder
orDecoder
. The API is for now considered experimental.
-
rustler_crates
configuration is deprecated in favor of explicitly passing options onuse Rustler
or configuring the module in yourconfig/*.exs
files. -
Env::send
andOwnedEnv::send_and_clear
will now return aResult
. Updating will thus introduce warnings about unusedResult
s. To remove the warnings without changing behaviour, theResult
s can be "used" aslet _ = env.send(...)
Neither the
Ok
nor theErr
case carry additional information so far. An error is returned if either the receiving or the sending process is dead. See also enif_send. -
As
Term::get_type
is now implemented usingenif_get_type
on all non-Windows systems, some cases of theTermType
enum
are changed, removed, or added (on all systems):EmptyList
is dropped,List
is returned for both empty and non-empty listsException
is droppedNumber
is split intoInteger
andFloat
(if NIF 2.14 support is explicitly enforced, onlyFloat
is returned)
-
The default NIF version is raised to 2.15 to make use of
enif_get_type
. To use a compiled NIF with an older version than OTP22, disable the default features and expliictly use thenif_version_2_14
feature in the library'sCargo.toml
:rustler = { version = "0.30", default-features = false, features = ["derive", "nif_version_2_14"] }
-
As noted for the
0.28 -> 0.29
transition below, the environment variableRUSTLER_NIF_VERSION
will not be considered anymore from 0.30 onwards.
RUSTLER_NIF_VERSION
is deprecated and will not be considered anymore for 0.30.
The NIF version will also not be guessed anymore from a potentially available
installed Erlang version. By default, NIF libraries will now be compiled against
NIF version 2.14 which is compatible down to OTP21. The default will be adjusted
along with the supported OTP versions.
If additional features are required that use newer NIF versions, these can be
included explicitly in the project's Cargo.toml
as, e.g.
[dependencies]
rustler = { version = "0.30", features = ["nif_version_2_17"] }
With this configuration, the resulting NIF library will only work from OTP26 onwards, but will also have access to the largest feature set.
MIX_ENV
is no longer considered for determining the build profile. Now, the
profile defaults to :release
. Use the :mode
option to pick another profile
explicitly. See #496.
0.22 changes how to define NIFs. Users upgrading to 0.22 should to do these things:
- Replace
rustler_atoms!
withrustler::atoms!
- Replace
resource_struct_init!
withrustler::resource!
- Replace
rustler::rustler_export_nifs!
withrustler::init!
- Use the new
rustler::nif
proc_macro to declare NIFs
Replacing rustler_atoms!
with rustler::atoms!
is fairly simple and already
sufficiently described in CHANGELOG.md. Similarly, replacing
resource_struct_init!
with rustler::resource!
is a simple rename, so this does
not need additional examples here.
rustler::init!
in combination with the new rustler::nif
proc_macro
simplifies exporting NIFs. Before, the NIFs and their arity needed to be specified
using tuple syntax:
rustler::rustler_export_nifs! {
"Elixir.Math",
[
("add", 2, add),
("long_running_operation", 0, long_running_operation, SchedulerFlags::DirtyCpu)
],
None
}
Now, listing the NIFs directly is sufficient:
rustler::init!("Elixir.Math", [add, long_running_operation]);
With this new macro, defining an on_load
function (e.g. to set up a resource with
rustler::resource!
), is done like this:
rustler::init!("Elixir.Math", [add, long_running_operation], load = a_function);
Note that NIF flags such as SchedulerFlags::DirtyCpu
are not declared in rustler::init!
, but
using the proc_macro rustler::nif
. See further below for information on migration NIF flags.
0.22 introduces a new proc_macro
allowing to spell out the parameter of a NIF
directly instead of using an args: &[Term<'a>]
. Lets consider an example add()
,
where the Elixir function looks like this:
def add(left, right), do: :erlang.nif_error(:not_loaded)
Previously, the signature of the corresponding NIF might have looked like this:
fn add<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error>
When calling the NIF from Elixir as add(1, 2)
, args
would then contain two
Term
, one for 1, and one for 2. With 0.22, this becomes more obvious, as the
NIFs signature resembles the Elixir function's signature:
#[rustler::nif]
fn add(a: i64, b: i64) -> i64
Under the hood, this is implemented by the rustler::nif
proc_macro. For the
new form to work, the parameters' types need to implement Decoder
, and the
return type needs to implement Encoder
.
Sometimes, we still need the environment Env
for the NIF. For example, if
work with Binary
and OwnedBinary
, the environment would be needed to create a Binary
from an OwnedBinary
. To allow this, env: Env<'a>
can be added explicitly as well:
#[rustler::nif]
pub fn map_entries_sorted<'a>(env: Env<'a>, iter: MapIterator<'a>) -> NifResult<Vec<Term<'a>>>
env
can then be used the same way as before.
The rustler::nif
proc_macro allows setting options directly on a NIF. Assume that we have a
NIF called _long_running_operation
, which used to be declared prior to Rustler v0.22 like this:
// Before
rustler::rustler_export_nifs! {
"Elixir.SomeNif",
[
// Note that the function in Rust is long_running_operation, but the NIF is exported as
// _long_running_operation!
("_long_running_operation", 0, long_running_operation, SchedulerFlags::DirtyCpu)
],
None
}
fn long_running_operation<'a>(env: Env<'a>, _args: &[Term<'a>]) -> Result<Term<'a>, Error> {
// ...
}
This definition declares that a function _long_running_operation
with arity
zero is to be exported, and that this function should be schedules on the
DirtyCpu
scheduler. With the changes in Rustler v0.22, the function would be declared like
this:
// Now
rustler::init!("Elixir.SomeNif", [long_running_operation]);
#[rustler::nif(
rename = "_long_running_operation",
schedule = "DirtyCpu"
)]
fn long_running_operation() -> TheProperReturnType {
// ..
}