-
Hi all, I'm trying to implement a NIF in Rust but I have serval issues/questions when using rustler. I would greatly appreciate any suggestions anyone may have for me as I don't quite think that I'm the right track for this... Related PR that attempts to do it in Rust elixir-explorer/explorer#931. Currently it's implemented in C here in elixir-explorer/explorer#930. Some background on thisI'm writing a Core lines from the C implementation: C Codestatic void *message_resource;
struct message {
ErlNifEnv *env;
ErlNifPid pid;
ERL_NIF_TERM value;
};
void destruct_local_message(ErlNifEnv *env, void *obj) {
struct message *m = (struct message *)obj;
enif_send(env, &m->pid, m->env, m->value);
enif_free_env(m->env);
}
ERL_NIF_TERM local_message_on_gc(ErlNifEnv *env, const ERL_NIF_TERM pid_term,
const ERL_NIF_TERM term) {
if (!enif_is_pid(env, pid_term)) {
return enif_make_badarg(env);
}
ErlNifPid pid;
if (!enif_get_local_pid(env, pid_term, &pid)) {
return kAtomError;
}
struct message *m =
enif_alloc_resource(message_resource, sizeof(struct message));
if (!m) {
return kAtomError;
}
m->env = enif_alloc_env();
if (!m->env) {
return kAtomError;
}
m->pid = pid;
m->value = enif_make_copy(m->env, term);
ERL_NIF_TERM res_term = enif_make_resource(env, m);
enif_release_resource(m);
return res_term;
}
void local_message_open_resource(ErlNifEnv *env) {
void *rt =
enif_open_resource_type(env, "Elixir.Explorer.Remote.LocalGC", "message",
destruct_local_message, ERL_NIF_RT_CREATE, 0);
if (!rt)
return;
message_resource = rt;
kAtomError = enif_make_atom(env, "error");
} Questions/IssuesWhen implementing it in Rust, the very first issue I have is, if I want to hold a value of a NIF term (passed from Elixir, assuming it can be any valid Erlang terms) in pub struct LocalMessage<'a> {
pub pid: LocalPid,
pub term: Term<'a>,
} and all subsequent structs that contains a pub struct ExLocalMessageRef<'a>(pub LocalMessage<'a>);
impl<'a> ExLocalMessageRef<'a> {
pub fn new(m: LocalMessage<'a>) -> Self {
Self(m)
}
} As a result, this requires me to mark the fn on_load<'a>(env: Env, _info: Term) -> bool {
rustler::resource!(ExLocalMessageRef<'a>, env);
true
} But the problem is, in static mut STRUCT_TYPE: Option<$crate::resource::ResourceType<$struct_name>> = None; I'm not sure if I should use lifetime |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
The reason this doesn't work is that it's not sound. You can't keep a pub struct LocalMessage {
pid: LocalPid,
env: OwnedEnv,
term: SavedTerm,
}
impl LocalMessage {
pub new<'a>(pid: LocalPid, term: Term<'a>) -> Self {
let env = OwnedEnv::new();
let term = env.save(term);
Self { env, pid, term }
}
// Consumes self
pub fn send(self) -> Result<(), SendError> {
self.env.send_and_clear(|env| env.load(self.term));
}
} It should be possible to put this object into a container in a resource. |
Beta Was this translation helpful? Give feedback.
-
Oh now I understand! Thank you very much @filmor! |
Beta Was this translation helpful? Give feedback.
The reason this doesn't work is that it's not sound. You can't keep a
Term<'a>
outside of the lifetime of the respectiveEnv<'a>
(which corresponds to aNifEnv*
). What you can do instead is creating anOwnedEnv
to keep aSavedTerm
around that you can later use in arun
orsend
call.I…