Rustler return and pass reference from Rust to Elixir and back to Rust

So… can’t you do this?

impl Encoder for MyStruct {
  fn encode(&self, env: Env) -> Term {
    <FIELD_OR_FIELDS>.encode(env)
  }
}

Nope I can’t. I can’t even compile this code. Maybe I should wait for better documentation on how to do it :3 Not sure if this was possible in earlier versions?

Dude, I’ve no way of telling you like that out of thin air. :003:

If your code is not secret, make a GitHub project and I will help.

Sure, sorry, repo is here :blush:

Ah, I see, I haven’t dealt with this. Have you checked these docs?

Apparently you also have to:

  1. Use Rust’s #[derive] directive;
  2. Make an Elixir module that represents your Rust struct.

I’d offer you to not use normal structs though. IMO a regular tuple should be fine.

Thanks a lot. Indeed I didn’t read the above section in docs. Unfortunately I couldn’t make it works. Tuples are not an option as I want to return reference to struct that is being returned by functions from the third party lib. But I will investigate this :slightly_smiling_face:

I can help with a PR but can’t promise anything quick, man, sorry. Way too busy these last several days.

1 Like

Unfortunately, no I was not able to solve this, yet. But after looking at your code I think we currently really have the same issue. So if I find something out, I’ll make sure to post an update here.

Meanwhile I’ll have to solve this a little differently for the time beeing (meaning: I’ll use a port) because I need a solution fast. But this won’t be ideal either, so I’ll come back to this approach later.

@dimitarvp thank you so much! :heart:

2 Likes

@mmmrrr @dimitarvp I probably solved my issue. It turned out I should use rustler::init! and rustler::resource! macros. Here is full code. It compiles so that’s something

  use rustler::resource::ResourceArc;
  use rustler::{Env, Term, NifStruct};
  
  rustler::atoms! {
      ok,
      error
  }
  
  rustler::init!("PineSSL", [add], load=load);
  
  #[derive(NifStruct)]
  #[module = "MyStruct"]
  pub struct MyStruct {
      pub a: i64
  }
  
  fn load(env: Env, _info: Term) -> bool {
      rustler::resource!(MyStruct, env);
      true
  }
  
  #[rustler::nif]
  fn add(a: i64, b: i64) -> ResourceArc<MyStruct> {
      ResourceArc::new(MyStruct{a: a+b})
  }

macro rustler::resource! implements ResourceTypeProvider trait for MyStruct and in rustler::init! I can invoke my function load which then invokes rustler::resource!.

2 Likes

Oh. But I thought this is included in the tutorial?

Sorry that I didn’t mention it, it’s kind of the default. But I should have!

It’s for sure included in example project

I was hitting the same error messages as others have shown here. I think the example project (this? github.com/rusterlium/NifIo) hasn’t been updated for the latest rustler version.

@mickel8 thanks for the example code, I was able to make some solid progress working forward from this.

I’m working on a table data structure for time-series analysis, and I have the basic parts working in rust, so now I’m working thru the integration process with elixir. If it helps anyone, you can see my code here.

After line 115 I have the rustler parts, all it does currently is instantiate structs that hold a mutable float, and there’s functions for update, read, and add. I had to keep things simple at first. Next I’ll be figuring out how to make this work with my SlidingWindow data structure.

2 Likes

I can’t figure out how to make rustler::resource!(SlidingWindow, env); work if SlidingWindow includes a field which requires a named lifetime parameter, such as either &[&str] or rustler::types::list::ListIterator.

First rust says,

error[E0726]: implicit elided lifetime not allowed here
   --> src/lib.rs:157:24
    |
157 |     rustler::resource!(SlidingWindow, env);
    |                        ^^^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>`

So we try that and then it says,

error[E0478]: lifetime bound not satisfied
   --> src/lib.rs:157:5
    |
157 |     rustler::resource!(SlidingWindow<'_>, env);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
note: lifetime parameter instantiated with the lifetime `'_` as defined on the impl at 157:38
   --> src/lib.rs:157:38
    |
157 |     rustler::resource!(SlidingWindow<'_>, env);
    |                                      ^^
    = note: but lifetime parameter must outlive the static lifetime
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

That last part seems like a clue: “note: this error originates in a macro”. And it makes sense, rust wants to know how long this reference will live for, but we’re trying to store it in an erlang term. Does anybody know how to resolve this?

Did you do impl Encoder with lifetime annotations?

Success! :tada:
Thanks for sharing your solution @mickel8 - that was really helpful. I missed the load function.

As a note to others: if you need to implement a custom decoder/encoder do not use the NifStruct proc macro. Otherwise you’ll get a “Conflicting implementations” error. NifStruct tries to implement those itself.

Last but not least one other thing: I had to wrap my connection reference with a Mutex. The ODBC connection in odbc-rs is not Syncable by default, and hence not threadsafe by itself.

2 Likes

I’m looking at the rustler source here: list.rs.html -- source

So the goal is to return my struct as a Term? Are there any examples of how to make a struct into a Term with a list field? I’m confused how these go together.

Looks like we have to implement ResourceTypeProvider in order to implement Encoder.

EDIT: Decided to seek some help from the rust community, here.

Sorry dude, waaaaaaaaay too busy lately, plus bad health. I’ll get back to you guys, I promise.

This is done for you when you do rustler::resource!(...). If it’s not then you have to do the same for another data structure before that as well.

Found a solution. I was using the wrong datatypes. It builds now, but it will take more work to optimize.

Can you share some more details?