From ca7936f5222ae45aebe0590e191f79cf11b0deef Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 13 Oct 2024 08:54:23 +0200 Subject: [PATCH 1/3] Add reference type and `make_ref` call --- rustler/src/lib.rs | 2 +- rustler/src/types/mod.rs | 9 +++- rustler/src/types/reference.rs | 52 +++++++++++++++++++ rustler_tests/lib/rustler_test.ex | 1 + .../native/rustler_test/src/test_env.rs | 9 +++- rustler_tests/test/env_test.exs | 9 ++++ 6 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 rustler/src/types/reference.rs diff --git a/rustler/src/lib.rs b/rustler/src/lib.rs index d1335526..159e7dc7 100644 --- a/rustler/src/lib.rs +++ b/rustler/src/lib.rs @@ -39,7 +39,7 @@ mod term; pub use crate::term::Term; pub use crate::types::{ Atom, Binary, Decoder, Encoder, ErlOption, ListIterator, LocalPid, MapIterator, NewBinary, - OwnedBinary, + OwnedBinary, Reference, }; #[cfg(feature = "big_integer")] diff --git a/rustler/src/types/mod.rs b/rustler/src/types/mod.rs index 52c141cf..c3f11ced 100644 --- a/rustler/src/types/mod.rs +++ b/rustler/src/types/mod.rs @@ -2,8 +2,6 @@ use crate::{Env, Error, NifResult, Term}; #[macro_use] pub mod atom; -pub mod i128; -pub mod path; pub use crate::types::atom::Atom; pub mod binary; @@ -32,6 +30,13 @@ pub mod tuple; pub mod local_pid; pub use self::local_pid::LocalPid; +#[doc(hidden)] +pub mod reference; +pub use self::reference::Reference; + +pub mod i128; +pub mod path; + pub mod truthy; pub mod elixir_struct; diff --git a/rustler/src/types/reference.rs b/rustler/src/types/reference.rs new file mode 100644 index 00000000..6ec526db --- /dev/null +++ b/rustler/src/types/reference.rs @@ -0,0 +1,52 @@ +use std::ops::Deref; + +use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; + +use crate::sys::enif_make_ref; + +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct Reference<'a>(Term<'a>); + +impl<'a> Deref for Reference<'a> { + type Target = Term<'a>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> From> for Term<'a> { + fn from(term: Reference<'a>) -> Self { + term.0 + } +} + +impl<'a> TryFrom> for Reference<'a> { + type Error = Error; + + fn try_from(term: Term<'a>) -> Result { + if term.is_ref() { + Ok(Reference(term)) + } else { + Err(Error::BadArg) + } + } +} + +impl<'a> Decoder<'a> for Reference<'a> { + fn decode(term: Term<'a>) -> NifResult { + term.try_into() + } +} + +impl<'a> Encoder for Reference<'a> { + fn encode<'b>(&self, env: Env<'b>) -> Term<'b> { + self.0.encode(env) + } +} + +impl<'a> Env<'a> { + pub fn make_ref(self) -> Reference<'a> { + unsafe { Reference(Term::new(self, enif_make_ref(self.as_c_arg()))) } + } +} diff --git a/rustler_tests/lib/rustler_test.ex b/rustler_tests/lib/rustler_test.ex index b2c2232f..bfebade6 100644 --- a/rustler_tests/lib/rustler_test.ex +++ b/rustler_tests/lib/rustler_test.ex @@ -103,6 +103,7 @@ defmodule RustlerTest do def whereis_pid(_), do: err() def is_process_alive(_), do: err() def sublists(_), do: err() + def make_refs(), do: err() def tuple_echo(_), do: err() def record_echo(_), do: err() diff --git a/rustler_tests/native/rustler_test/src/test_env.rs b/rustler_tests/native/rustler_test/src/test_env.rs index 06dd63d6..99f9da4f 100644 --- a/rustler_tests/native/rustler_test/src/test_env.rs +++ b/rustler_tests/native/rustler_test/src/test_env.rs @@ -2,7 +2,7 @@ use rustler::env::{OwnedEnv, SavedTerm, SendError}; use rustler::types::atom; use rustler::types::list::ListIterator; use rustler::types::LocalPid; -use rustler::{Atom, Encoder, Env, NifResult, Term}; +use rustler::{Atom, Encoder, Env, NifResult, Reference, Term}; use std::thread; // Send a message to several PIDs. @@ -84,3 +84,10 @@ pub fn sublists<'a>(env: Env<'a>, list: Term<'a>) -> NifResult { Ok(atom::ok()) } + +#[rustler::nif] +fn make_refs<'a>(env: Env<'a>) -> (bool, Reference<'a>, Reference<'a>) { + let first = env.make_ref(); + let second = env.make_ref(); + (first != second, first, second) +} diff --git a/rustler_tests/test/env_test.exs b/rustler_tests/test/env_test.exs index 29c06acc..6e016bbd 100644 --- a/rustler_tests/test/env_test.exs +++ b/rustler_tests/test/env_test.exs @@ -104,4 +104,13 @@ defmodule RustlerTest.EnvTest do assert :error == RustlerTest.send(task.pid, :msg) assert :error == RustlerTest.send(task.pid, :msg) end + + test "make_ref" do + {different, ref1, ref2} = RustlerTest.make_refs() + + assert different + assert is_reference(ref1) + assert is_reference(ref2) + assert ref1 != ref2 + end end From 9bd4d6031614e4ccf6f48e7261be0115508398f3 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 13 Oct 2024 09:12:28 +0200 Subject: [PATCH 2/3] Add comments and in_env variant returning a ref again --- rustler/src/types/reference.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rustler/src/types/reference.rs b/rustler/src/types/reference.rs index 6ec526db..013e3263 100644 --- a/rustler/src/types/reference.rs +++ b/rustler/src/types/reference.rs @@ -4,9 +4,20 @@ use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; use crate::sys::enif_make_ref; +/// Wrapper for BEAM reference terms. #[derive(PartialEq, Eq, Clone, Copy)] pub struct Reference<'a>(Term<'a>); +impl<'a> Reference<'a> { + /// Returns a representation of self in the given Env. + /// + /// If the term is already is in the provided env, it will be directly returned. Otherwise + /// the term will be copied over. + pub fn in_env<'b>(&self, env: Env<'b>) -> Reference<'b> { + Reference(self.0.in_env(env)) + } +} + impl<'a> Deref for Reference<'a> { type Target = Term<'a>; @@ -46,6 +57,7 @@ impl<'a> Encoder for Reference<'a> { } impl<'a> Env<'a> { + /// Create a new reference in this environment pub fn make_ref(self) -> Reference<'a> { unsafe { Reference(Term::new(self, enif_make_ref(self.as_c_arg()))) } } From 82d37f340db110f84536635d4c4a69e15727925a Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 13 Oct 2024 10:29:17 +0200 Subject: [PATCH 3/3] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0a24870..684365f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ versions. - Floats can be decoded from integers (#641, fixes #603) - Resource types can implement and use dynamic calls on NIF version 2.16 (#635) - `Encoder` and `Decoder` implementations for `Box` (#644) +- `Reference` type and `env.make_ref()` function (#657) ### Fixed