serde_rustler
provides a Serde Serializer and Deserializer for Rustler types, so you can easily serialize and deserialize native Rust types directly to and from native Elixir terms within your NIFs.
Install from Crates.io:
toml
[dependencies]
serde_rustler = "0.0.2"
Below is an example of how you might use serde_rustler
within a rust NIF:
```rust
extern crate rustler;
use rustler::{Env, error::Error as NifError, NifResult, Term}; use serde::{Serialize, Deserialize}; use serderustler::{fromterm, to_term};
rustlerexportnifs! { "Elixir.SerdeNif", [("readme", 1, readme)], None }
// NOTE: to serialize to the correct Elixir record, you MUST tell serde to // rename the variants to the full Elixir record module atom.
enum AnimalType { #[serde(rename = "Elixir.SerdeNif.AnimalType.Cat")] Cat(String), #[serde(rename = "Elixir.SerdeNif.AnimalType.Dog")] Dog(String), }
// NOTE: to serialize to an actual Elixir struct (rather than a just map with // a :struct key), you MUST tell serde to rename the struct to the full // Elixir struct module atom.
struct Animal {
#[serde(rename = "type")]
_type: AnimalType,
name: String,
age: u8,
owner: Option
fn readme<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult
println!("serialized animal: {:?}", animal);
to_term(env, animal).or(Err(NifError::BadArg))
} ```
Corresponding Elixir code (code structure, import
s, alias
es and require
s simplified or omitted for brevity):
```elixir defmodule SerdeNif do use Rustler, otpapp: :serdenif
def readme(term), do: :erlang.niferror(:nifnotloaded)
defmodule Animal do @type t :: %Animal{ type: Cat.t() | Dog.t(), name: bitstring, age: pos_integer, owner: nil | bitstring } defstruct type: Cat.record(), name: "", age: 0, owner: nil
@doc "Deserializes term as a Rust `Animal` struct, then serializes it back into an Elixir `Animal` struct. Should return true."
def test() do
animal = %Animal{
type: Animal.Cat.record(),
name: "Garfield",
age: 41,
}
SerdeNif.readme(animal) == animal
end
end
defmodule AnimalType.Cat do require Record @type t {MODULE, String.t()} Record.defrecord(:record, MODULE, breed: "tabby") end
defmodule AnimalType.Dog do require Record @type t {MODULE, String.t()} Record.defrecord(:record, :Dog, breed: "mutt") end end ```
| Type Name | Serde (Rust) Values | Elixir Terms (default behaviour) |
|-----------|------------------|---------------------|
| bool | true
or false
| true
or false
|
| 1 number | i8
, i16
, i32
, i64
, u8
, u16
, u32
, u64
, f32
, f64
(TODO: i128
and u128
) | number
|
| 1 char | ""
| bitstring
|
| string | ""
| bitstring
|
| 2 byte array | &[u8]
or Vec<u8>
| <<_::_*8>>
|
| option | Some(T)
or None
| T
or :nil
|
| unit | None
| :nil
|
| unit struct | struct Unit
| :nil
|
| 3 unit variant | E::A
in enum UnitVariant { A }
| :A
|
| 3 newtype struct | struct Millimeters(u8)
| {:Millimeters, u8}
|
| 3 newtype variant | E::N
in enum E { N(u8) }
| {:N, u8}
|
| newtype variant (any Ok
and Err
tagged enum) | enum R<T, E> { Ok(T), Err(E) }
| {:ok, T}
or {:error, E}
|
| seq | Vec<T>
| [T]
|
| tuple | (u8,)
| {u8,}
|
| 3 tuple struct | struct Rgb(u8, u8, u8)
| {:Rgb, u8, u8, u8}
|
| 3 tuple variant | E::T
in enum E { T(u8, u8) }
| {:T, u8, u8}
|
| 1 map | HashMap<K, V>
| %{}
|
| 3 struct | struct Rgb { r: u8, g: u8, b: u8 }
| %Rgb{ r: byte, g: byte, b: byte }
|
| 3 struct variant | E::S
in enum E { Rgb { r: u8, g: u8, b: u8 } }
| %Rgb{ r: byte, g: byte, b: byte }
|
1: API still being decided / implemented.
2: Requires specifying a specific serialize implementation, such as serde_bytes
.
3: When serializing unknown input to terms, atoms will not be created and will instead be replaced with Elixir bitstrings. Therefore "records" will be tuples ({bitstring, ...}
) and "structs" will be maps containing %{:__struct__ => bitstring}
. The unfortunate consequence of this is that deserialize_any
will lack the necessary information needed deserialize many terms without type hints, such as structs
, enums
and enum variants
, and tuples
. (Feedback on how best to solve this is very welcome here).
i128
and u128
| Version | Change Summary |
| ------- | ---------------|
| v0.0.2 | cleanup, better deserialize_any
support |
| v0.0.1 | initial release |
git checkout -b feature/fooBar
)git commit -am 'Add some fooBar'
)git push origin feature/fooBar
)MIT