arena_lang/id.rs
1//! The typed handle into an arena.
2
3use core::cmp::Ordering;
4use core::fmt;
5use core::hash::{Hash, Hasher};
6use core::marker::PhantomData;
7
8/// A small, copyable, type-tagged handle to one value in an [`Arena`](crate::Arena).
9///
10/// An `Id<T>` is the stable way to refer to an allocated value: it stays valid for
11/// the life of the arena that issued it, and it is a single `u32` — four bytes,
12/// the same as a bare index — so passing one is no more expensive than passing an
13/// integer. Unlike a `&T`, it carries no borrow, so a tree of nodes can store ids
14/// pointing at one another without tangling the borrow checker — which is exactly
15/// why an AST or IR holds child handles rather than child references.
16///
17/// The `T` tag is compile-time only (it occupies no space): it stops an
18/// `Id<Expr>` from being passed where an `Id<Stmt>` is expected, so a handle can
19/// only ever index the arena that produced it. `Id<T>` is `Copy`, `Eq`, `Ord`, and
20/// `Hash` for **every** `T` — the tag never adds a bound — so it works as a map key
21/// regardless of what it points at.
22///
23/// Resolve a handle with [`Arena::get`](crate::Arena::get) /
24/// [`get_mut`](crate::Arena::get_mut); there is no public constructor, so an `Id`
25/// can only come from an [`Arena::alloc`](crate::Arena::alloc).
26///
27/// # Examples
28///
29/// ```
30/// use arena_lang::Arena;
31///
32/// let mut arena = Arena::new();
33/// let id = arena.alloc("node");
34///
35/// // The handle round-trips back to the value it named.
36/// assert_eq!(arena.get(id), Some(&"node"));
37///
38/// // It is Copy and four bytes wide, whatever it points at.
39/// let also = id;
40/// assert_eq!(id, also);
41/// assert_eq!(core::mem::size_of_val(&id), 4);
42/// ```
43pub struct Id<T> {
44 raw: u32,
45 /// Compile-time type tag. `fn() -> T` keeps `Id<T>` `Copy`, `Send`, and `Sync`
46 /// for any `T`, and covariant in `T`, without ever borrowing or owning a `T`.
47 marker: PhantomData<fn() -> T>,
48}
49
50impl<T> Id<T> {
51 /// Wraps a raw arena index. Internal: only an arena mints handles, so that the
52 /// type tag always matches the arena the handle indexes.
53 #[inline]
54 pub(crate) const fn new(raw: u32) -> Self {
55 Self {
56 raw,
57 marker: PhantomData,
58 }
59 }
60
61 /// Returns the raw arena index this handle wraps.
62 #[inline]
63 pub(crate) const fn raw(self) -> u32 {
64 self.raw
65 }
66}
67
68// The trait impls are written by hand rather than derived: a derive would bound
69// each impl on `T` (e.g. `T: Clone`), but a handle must be `Copy`, comparable, and
70// hashable for every `T`, since `T` is only a compile-time tag.
71
72impl<T> Clone for Id<T> {
73 #[inline]
74 fn clone(&self) -> Self {
75 *self
76 }
77}
78
79impl<T> Copy for Id<T> {}
80
81impl<T> PartialEq for Id<T> {
82 #[inline]
83 fn eq(&self, other: &Self) -> bool {
84 self.raw == other.raw
85 }
86}
87
88impl<T> Eq for Id<T> {}
89
90impl<T> PartialOrd for Id<T> {
91 #[inline]
92 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
93 Some(self.cmp(other))
94 }
95}
96
97impl<T> Ord for Id<T> {
98 #[inline]
99 fn cmp(&self, other: &Self) -> Ordering {
100 self.raw.cmp(&other.raw)
101 }
102}
103
104impl<T> Hash for Id<T> {
105 #[inline]
106 fn hash<H: Hasher>(&self, state: &mut H) {
107 self.raw.hash(state);
108 }
109}
110
111impl<T> fmt::Debug for Id<T> {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 write!(f, "Id({})", self.raw)
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 extern crate alloc;
120 use alloc::collections::BTreeSet;
121 use alloc::format;
122
123 use crate::Arena;
124
125 #[test]
126 fn test_id_traits_do_not_depend_on_the_tag() {
127 // A tag that is neither `Clone` nor `Eq` must not stop `Id` from being
128 // `Copy`, `Eq`, and `Debug` — the tag is compile-time only.
129 struct NotClone;
130 let mut arena = Arena::<NotClone>::new();
131 let id = arena.alloc(NotClone);
132 let copy = id; // Copy
133 assert_eq!(id, copy); // Eq
134 assert!(format!("{id:?}").starts_with("Id")); // Debug
135 }
136
137 #[test]
138 fn test_distinct_allocations_have_distinct_ids() {
139 let mut arena = Arena::new();
140 let ids: BTreeSet<_> = (0..16).map(|i| arena.alloc(i)).collect();
141 assert_eq!(ids.len(), 16); // all distinct, usable as ordered-set keys
142 }
143
144 #[test]
145 fn test_id_is_four_bytes_for_any_element() {
146 assert_eq!(core::mem::size_of::<crate::Id<u8>>(), 4);
147 assert_eq!(core::mem::size_of::<crate::Id<[u128; 4]>>(), 4);
148 }
149}