Skip to content

Commit d658ae2

Browse files
authored
Add reference type and make_ref call (#657)
* Add reference type and `make_ref` call * Add comments and in_env variant returning a ref again * Add changelog entry
1 parent 885e56a commit d658ae2

File tree

7 files changed

+91
-4
lines changed

7 files changed

+91
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ versions.
1717
- Floats can be decoded from integers (#641, fixes #603)
1818
- Resource types can implement and use dynamic calls on NIF version 2.16 (#635)
1919
- `Encoder` and `Decoder` implementations for `Box<T>` (#644)
20+
- `Reference` type and `env.make_ref()` function (#657)
2021

2122
### Fixed
2223

rustler/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ mod term;
3939
pub use crate::term::Term;
4040
pub use crate::types::{
4141
Atom, Binary, Decoder, Encoder, ErlOption, ListIterator, LocalPid, MapIterator, NewBinary,
42-
OwnedBinary,
42+
OwnedBinary, Reference,
4343
};
4444

4545
#[cfg(feature = "big_integer")]

rustler/src/types/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ use crate::{Env, Error, NifResult, Term};
22

33
#[macro_use]
44
pub mod atom;
5-
pub mod i128;
6-
pub mod path;
75
pub use crate::types::atom::Atom;
86

97
pub mod binary;
@@ -32,6 +30,13 @@ pub mod tuple;
3230
pub mod local_pid;
3331
pub use self::local_pid::LocalPid;
3432

33+
#[doc(hidden)]
34+
pub mod reference;
35+
pub use self::reference::Reference;
36+
37+
pub mod i128;
38+
pub mod path;
39+
3540
pub mod truthy;
3641

3742
pub mod elixir_struct;

rustler/src/types/reference.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use std::ops::Deref;
2+
3+
use crate::{Decoder, Encoder, Env, Error, NifResult, Term};
4+
5+
use crate::sys::enif_make_ref;
6+
7+
/// Wrapper for BEAM reference terms.
8+
#[derive(PartialEq, Eq, Clone, Copy)]
9+
pub struct Reference<'a>(Term<'a>);
10+
11+
impl<'a> Reference<'a> {
12+
/// Returns a representation of self in the given Env.
13+
///
14+
/// If the term is already is in the provided env, it will be directly returned. Otherwise
15+
/// the term will be copied over.
16+
pub fn in_env<'b>(&self, env: Env<'b>) -> Reference<'b> {
17+
Reference(self.0.in_env(env))
18+
}
19+
}
20+
21+
impl<'a> Deref for Reference<'a> {
22+
type Target = Term<'a>;
23+
24+
fn deref(&self) -> &Self::Target {
25+
&self.0
26+
}
27+
}
28+
29+
impl<'a> From<Reference<'a>> for Term<'a> {
30+
fn from(term: Reference<'a>) -> Self {
31+
term.0
32+
}
33+
}
34+
35+
impl<'a> TryFrom<Term<'a>> for Reference<'a> {
36+
type Error = Error;
37+
38+
fn try_from(term: Term<'a>) -> Result<Self, Self::Error> {
39+
if term.is_ref() {
40+
Ok(Reference(term))
41+
} else {
42+
Err(Error::BadArg)
43+
}
44+
}
45+
}
46+
47+
impl<'a> Decoder<'a> for Reference<'a> {
48+
fn decode(term: Term<'a>) -> NifResult<Self> {
49+
term.try_into()
50+
}
51+
}
52+
53+
impl<'a> Encoder for Reference<'a> {
54+
fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
55+
self.0.encode(env)
56+
}
57+
}
58+
59+
impl<'a> Env<'a> {
60+
/// Create a new reference in this environment
61+
pub fn make_ref(self) -> Reference<'a> {
62+
unsafe { Reference(Term::new(self, enif_make_ref(self.as_c_arg()))) }
63+
}
64+
}

rustler_tests/lib/rustler_test.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ defmodule RustlerTest do
103103
def whereis_pid(_), do: err()
104104
def is_process_alive(_), do: err()
105105
def sublists(_), do: err()
106+
def make_refs(), do: err()
106107

107108
def tuple_echo(_), do: err()
108109
def record_echo(_), do: err()

rustler_tests/native/rustler_test/src/test_env.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rustler::env::{OwnedEnv, SavedTerm, SendError};
22
use rustler::types::atom;
33
use rustler::types::list::ListIterator;
44
use rustler::types::LocalPid;
5-
use rustler::{Atom, Encoder, Env, NifResult, Term};
5+
use rustler::{Atom, Encoder, Env, NifResult, Reference, Term};
66
use std::thread;
77

88
// Send a message to several PIDs.
@@ -84,3 +84,10 @@ pub fn sublists<'a>(env: Env<'a>, list: Term<'a>) -> NifResult<Atom> {
8484

8585
Ok(atom::ok())
8686
}
87+
88+
#[rustler::nif]
89+
fn make_refs<'a>(env: Env<'a>) -> (bool, Reference<'a>, Reference<'a>) {
90+
let first = env.make_ref();
91+
let second = env.make_ref();
92+
(first != second, first, second)
93+
}

rustler_tests/test/env_test.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,13 @@ defmodule RustlerTest.EnvTest do
104104
assert :error == RustlerTest.send(task.pid, :msg)
105105
assert :error == RustlerTest.send(task.pid, :msg)
106106
end
107+
108+
test "make_ref" do
109+
{different, ref1, ref2} = RustlerTest.make_refs()
110+
111+
assert different
112+
assert is_reference(ref1)
113+
assert is_reference(ref2)
114+
assert ref1 != ref2
115+
end
107116
end

0 commit comments

Comments
 (0)