I am currently using Rustler, a tool that allows for Elixir to interact with the Rust ecosystem. As a newcomer to Rust, working on Rust-Elixir crossover libraries helps me understand its quirks.
Today, I was trying to use a SQL parser in Rust and convert the results to Elixir terms. Rustler’s NifStructs feature made this process convenient. However, I encountered an issue with a data structure defined as follows:
#[derive(NifStruct)]
#[module = "SqlParser.Select"]
pub struct Select {
pub selection: Option<Expr>
}
The issue arose when I encountered this enum:
pub enum Expr {
BinaryOp {
left: Box<Expr>,
op: BinaryOperator,
right: Box<Expr>,
}
}
Because of the Box<Expr>
, this becomes a recursive data structure. To encode this with Rustler, I needed to implement the Encoder
trait for Box<Expr>
.
impl Encoder for Box<Expr> {
fn encode<'a>(&self, env: Env<'a>) -> Term<'a> {
let data =&** self;
data.encode(env)
}
}
However, this caused a chain reaction. The Select
struct above has a Decoder
trait implemented for it, and it expects Expr
to also implement that trait to decode. At first, I implemented the Decoder
trait for Expr
, but since I only need to use the parser one-way (from SQL through Rust to Elixir) and not vice-versa, this was not necessary.
I had to do a bit of digging but found that I could use:
#[derive(NifStruct)]
#[rustler(encode)]
#[module = "SqlParser.Select"]
and the Select
struct would never implement the Decoder
trait, resulting in that the Expr
also did not need to implement it.
So annotate the code with #[rustler(encode)]
or #[rustler(decode)]
if you only need one-way
encoding/decoding in Rustler.