DEV Community

Maarten van Vliet
Maarten van Vliet

Posted on

TIL - Implementing Encoder for NifStructs

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>
}
Enter fullscreen mode Exit fullscreen mode

The issue arose when I encountered this enum:

pub enum Expr {
    BinaryOp {
        left: Box<Expr>,
        op: BinaryOperator,
        right: Box<Expr>,
    }
}
Enter fullscreen mode Exit fullscreen mode

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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)