arid/lib.rs
1//! An ergonomic object-model for Rust.
2//!
3//! Behold, a doubly-linked-list! No `RefCell`s are required despite the data-structure being full
4//! of reference cycles.
5//!
6//! ```
7//! use arid::{Object as _, Handle as _, object, Strong, W};
8//!
9//! #[derive(Debug)]
10//! pub struct List {
11//! head: Option<Strong<NodeHandle>>,
12//! tail: Option<NodeHandle>,
13//! }
14//!
15//! #[derive(Debug)]
16//! pub struct Node {
17//! list: Option<ListHandle>,
18//! value: u32,
19//! prev: Option<NodeHandle>,
20//! next: Option<Strong<NodeHandle>>,
21//! }
22//!
23//! object!(pub List, pub Node);
24//!
25//! impl ListHandle {
26//! pub fn new(w: W) -> Strong<Self> {
27//! List { head: None, tail: None }.spawn(w)
28//! }
29//!
30//! pub fn add_after(self, prev: Option<NodeHandle>, node: NodeHandle, w: W) {
31//! // Validate operation.
32//! assert!(node.r(w).list.is_none());
33//! assert!(prev.is_none_or(|prev| prev.r(w).list == Some(self)));
34//!
35//! node.m(w).list = Some(self);
36//!
37//! // Establish links with the previous node.
38//! node.m(w).prev = prev; // prev <- node
39//!
40//! // prev -> node
41//! let node_strong = node.as_strong(w);
42//! let next = if let Some(prev) = prev {
43//! prev.m(w).next.replace(node_strong)
44//! } else {
45//! self.m(w).head = Some(node_strong);
46//! None
47//! };
48//!
49//! // Establish links with the next node.
50//! // node <- next
51//! if let Some(next) = next.as_ref() {
52//! next.m(w).prev = Some(node);
53//! } else {
54//! self.m(w).tail = Some(node);
55//! }
56//!
57//! node.m(w).next = next; // node -> next
58//! }
59//! }
60//! ```
61//!
62//! # Motivation
63//!
64//! The core idea behind `arid` is to tie all object borrows to some parent [`World`] instance. That
65//! is, we make each smart-pointer accessible by methods such as these...
66//!
67//! ```
68//! # struct World;
69//! # struct MyObjectHandle;
70//! # struct MyObject;
71//! impl MyObjectHandle {
72//! /// Borrow the smart-pointer's value immutably.
73//! fn r<'w>(self, world: &'w World) -> &'w MyObject {
74//! # /*
75//! ...
76//! # */ todo!()
77//! }
78//!
79//! /// Borrow the smart-pointer's value mutably.
80//! fn m<'w>(self, world: &'w mut World) -> &'w mut MyObject {
81//! # /*
82//! ...
83//! # */ todo!()
84//! }
85//! }
86//! ```
87//!
88//! One major implication of this model is that no borrows have to be validated at runtime,
89//! eliminating an entire class of runtime bugs and reducing runtime overhead slightly. This is
90//! especially valuable since runtime borrow checker violations can happen at a distance, may
91//! not always be exercised under all circumstances, and can be introduced invisibly depending on
92//! the timing of [`Ref`](std::cell::Ref) guard drops.
93//!
94//! The other major implication of this model, however, is that *only one object can be borrowed at
95//! a time!* The "magic" of `arid`, then, is the way it hides this restriction in practice. We do
96//! this in three main ways:
97//!
98//! 1. First, we make smart pointers (a.k.a handles) [`Copy`]able.
99//!
100//! This removes one incentive for users to create long-term borrows from a dereferenced object
101//! since handles can be implicitly copied out of the dereferenced object without needing an
102//! explicit `.clone()`. By making more borrow sites short-lived, we avoid a large number of
103//! borrow checker violations which may otherwise crop up from our strict "single borrow at a
104//! time" restriction.
105//!
106//! 2. Second, we allow smart pointers to be receivers on `impl` blocks.
107//!
108//! This allows users to interweave multiple mutable borrows within a single method body while
109//! still keeping a subject-verb-style calling syntax for object methods.
110//!
111//! 3. Third, we set a convention to always pass the `world` at the end of each argument list.
112//!
113//! This is quite important since arguments are evaluated in the order they appear in a function
114//! call. If, instead, we passed the `world` in the first argument of a call expression, the
115//! subsequent argument expressions would have to contend with a concurrent borrow in that first
116//! argument.
117//!
118//! These three decisions placate the borrow checker for most usage patterns, making the system
119//! quite ergonomic compared to its more traditional alternatives.
120//!
121//! # Basic Usage
122//!
123//! All object instances in the `arid` object model are owned by exactly one [`World`]. It can be
124//! instantiated anywhere with...
125//!
126//! ```
127//! use arid::World;
128//!
129//! let mut w = World::new();
130//! let w = &mut w;
131//! ```
132//!
133//! By convention, we try to ensure that the `world` for any given function body is named `w` and
134//! corresponds to an (im)mutable borrow of the world.
135//!
136//! We can then define the object types which live inside a world using the [`object!`] macro like
137//! so...
138//!
139//! ```
140//! use arid::object;
141//!
142//! #[derive(Debug)]
143//! pub struct MyObject {
144//! count: u32,
145//! }
146//!
147//! object!(pub MyObject);
148//! ```
149//!
150//! The `object!` macro takes the name of a structure within the current scope (e.g. `MyObject`) and
151//! does a couple things...
152//!
153//! - It implements the [`Object`] trait for the target type `MyObject`.
154//! - It defines a newtype for handles of that object and calls the newtype `<StructName>Handle`
155//! (in our case, `MyObjectHandle`). The visibility of this newtype is taken from the macro
156//! invocation and must match the visibility of the value structure.
157//! - It implements the [`Handle`] trait for that handle newtype structure.
158//!
159//! The requirements for defining an object are minimal: it must be [`Sized`], live for `'static`,
160//! and implement [`Debug`](std::fmt::Debug).
161//!
162//! The `Object` trait exposes an [`Object::spawn`] method to allocate an object instance into a
163//! given `World`. We can use it like so...
164//!
165//! ```
166//! # use arid::World;
167//! #
168//! # let mut w = World::new();
169//! # let w = &mut w;
170//! #
171//! # use arid::object;
172//! #
173//! # #[derive(Debug)]
174//! # pub struct MyObject {
175//! # count: u32,
176//! # }
177//! #
178//! # object!(pub MyObject);
179//! #
180//! use arid::Object as _;
181//!
182//! let my_counter = MyObject { count: 1 }.spawn(w);
183//! ```
184//!
185//! We can then access the handle's value immutably using the [`Handle::r`] method and mutably using
186//! the [`Handle::m`] method.
187//!
188//! ```
189//! # use arid::World;
190//! #
191//! # let mut w = World::new();
192//! # let w = &mut w;
193//! #
194//! # use arid::object;
195//! #
196//! # #[derive(Debug)]
197//! # pub struct MyObject {
198//! # count: u32,
199//! # }
200//! #
201//! # object!(pub MyObject);
202//! #
203//! # use arid::Object as _;
204//! #
205//! # let my_counter = MyObject { count: 1 }.spawn(w);
206//! use arid::Handle as _;
207//!
208//! my_counter.m(w).count += 1;
209//! assert_eq!(my_counter.r(w).count, 2);
210//! ```
211//!
212//! Since each object's corresponding handle newtype is declared in the crate which invoked the
213//! `object!` macro, we are allowed to implement inherent methods and traits directly onto the
214//! handle.
215//!
216//! ```
217//! # use arid::World;
218//! #
219//! # let mut w = World::new();
220//! # let w = &mut w;
221//! #
222//! # use arid::object;
223//! #
224//! # #[derive(Debug)]
225//! # pub struct MyObject {
226//! # count: u32,
227//! # }
228//! #
229//! # object!(pub MyObject);
230//! #
231//! # use arid::Object as _;
232//! #
233//! # let my_counter = MyObject { count: 1 }.spawn(w);
234//! # use arid::Handle as _;
235//! #
236//! # my_counter.m(w).count += 1;
237//! # assert_eq!(my_counter.r(w).count, 2);
238//! use arid::{W, Wr};
239//!
240//! impl MyObjectHandle {
241//! pub fn increment(self, w: W) {
242//! self.m(w).count += 1;
243//! }
244//!
245//! pub fn is_less_than(self, other: u32, w: Wr) -> bool {
246//! self.r(w).count < other
247//! }
248//! }
249//!
250//! assert!(my_counter.is_less_than(3, w));
251//! my_counter.increment(w);
252//! assert!(!my_counter.is_less_than(3, w));
253//! ```
254//!
255//! Note that [`W`] is just an alias to a `&mut World` and [`Wr`] is just an alias to a `&World`.
256//! Rust allows you to elide the lifetime of these type aliases in most cases. Rust implicitly
257//! reborrows references when they're passed directly to a function, which allows us to avoid
258//! explicit `&mut *w` and `&*w` reborrowing syntax.
259//!
260//! <div class="warning">
261//!
262//! Also note that, by convention, ***the `world` parameter always goes last*** to help the borrow
263//! checker understand more valid code.
264//!
265//! <details><summary><strong style="cursor: pointer">Justification</strong></summary>
266//!
267//! This convention is valuable because Rust always evaluates function call arguments in their
268//! syntactic order. If the world were to be passed first, the function's borrow of that world would
269//! happen before all subsequent arguments were evaluated, preventing those arguments from borrowing
270//! the world mutably.
271//!
272//! This code disrespects the conventions and gets a borrow checker error:
273//!
274//! ```compile_fail
275//! # use arid::World;
276//! #
277//! # let mut w = World::new();
278//! # let w = &mut w;
279//! #
280//! # use arid::object;
281//! #
282//! # #[derive(Debug)]
283//! # pub struct MyObject {
284//! # count: u32,
285//! # }
286//! #
287//! # object!(pub MyObject);
288//! #
289//! # use arid::Object as _;
290//! #
291//! # let my_counter = MyObject { count: 1 }.spawn(w);
292//! # use arid::Handle as _;
293//! #
294//! # my_counter.m(w).count += 1;
295//! # assert_eq!(my_counter.r(w).count, 2);
296//! # use arid::{W, Wr};
297//! impl MyObjectHandle {
298//! pub fn increment_by(self, w: W, delta: u32) {
299//! self.m(w).count += delta;
300//! }
301//! }
302//!
303//! // Double the count!
304//! my_counter.increment_by(w, my_counter.m(w).count);
305//! ```
306//!
307//! ```text
308//! error[E0499]: cannot borrow `*w` as mutable more than once at a time
309//! --> convention.rs:33:41
310//! |
311//! 33 | my_counter.increment_by(w, my_counter.m(w).count);
312//! | ------------ - ^ second mutable borrow occurs here
313//! | | |
314//! | | first mutable borrow occurs here
315//! | first borrow later used by call
316//! |
317//! help: try adding a local storing this argument...
318//! --> convention.rs:33:28
319//! |
320//! 33 | my_counter.increment_by(w, my_counter.m(w).count);
321//! | ^^^^^^^^^^^^^^^
322//! help: ...and then using that local as the argument to this call
323//! --> convention.rs:33:1
324//! |
325//! 33 | my_counter.increment_by(w, my_counter.m(w).count);
326//! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
327//! ```
328//!
329//! If we reordered the arguments, the error would go away!
330//!
331//! ```
332//! # use arid::World;
333//! #
334//! # let mut w = World::new();
335//! # let w = &mut w;
336//! #
337//! # use arid::object;
338//! #
339//! # #[derive(Debug)]
340//! # pub struct MyObject {
341//! # count: u32,
342//! # }
343//! #
344//! # object!(pub MyObject);
345//! #
346//! # use arid::Object as _;
347//! #
348//! # let my_counter = MyObject { count: 1 }.spawn(w);
349//! # use arid::Handle as _;
350//! #
351//! # my_counter.m(w).count += 1;
352//! # assert_eq!(my_counter.r(w).count, 2);
353//! # use arid::{W, Wr};
354//! impl MyObjectHandle {
355//! pub fn increment_by(self, delta: u32, w: W) {
356//! self.m(w).count += delta;
357//! }
358//! }
359//!
360//! // Double the count!
361//! my_counter.increment_by(my_counter.m(w).count, w);
362//! ```
363//!
364//! </details>
365//! </div>
366//!
367//! To pretty-print a handle's value using [`Debug`](std::fmt::Debug), you must first wrap the
368//! target handle in a [`WorldDebug`] wrapper type using the [`Handle::debug`] method. This wrapper
369//! calls into the handle's regular `Debug::fmt` method but passes the [`World`] through
370//! thread-local storage so the printed value may be accessed.
371//!
372//! The debug-printing logic for all `Handle`s ensures that cyclic formatting is handled properly.
373//! For example, printing the following structure will not cause the program to stack-overflow:
374//!
375//! ```
376//! # use arid::World;
377//! #
378//! # let mut w = World::new();
379//! # let w = &mut w;
380//! use arid::{Handle as _, Object as _, object};
381//!
382//! #[derive(Debug)]
383//! pub struct Cycle {
384//! other: Option<CycleHandle>,
385//! }
386//!
387//! object!(pub Cycle);
388//!
389//! let foo = Cycle { other: None }.spawn(w);
390//! let bar = Cycle { other: None }.spawn(w);
391//!
392//! foo.m(w).other = Some(*bar);
393//! bar.m(w).other = Some(*foo);
394//!
395//! dbg!(foo.debug(w));
396//! ```
397//!
398//! ```text
399//! [debug.rs:16:5] foo.debug(w) = debug::main::Cycle[0, v1]: Cycle {
400//! other: Some(
401//! debug::main::Cycle[1, v1]: Cycle {
402//! other: Some(
403//! debug::main::Cycle[0, v1],
404//! ),
405//! },
406//! ),
407//! }
408//! ```
409//!
410//! # Lifecycle
411//!
412//! Objects in `arid` are reference-counted although their semantics are a bit special:
413//!
414//! - The [`Handle`] newtypes generated by the `object!` macro are weak references but are assumed
415//! to be valid. As such, you can call `.r()` and `.w()` on them directly. These types are
416//! `Copy`able.
417//! - [`Strong`] wrappers around handles are, as their name suggest, strong references. These
418//! objects [`Deref`](std::ops::Deref) to their underlying weak `Handle` newtype. These types are
419//! *not* `Copy`able but are `Clone`able.
420//! - [`MayDangle`] are wrappers around handle newtypes which force the user to explicitly check for
421//! dangling values using [`MayDangle::get`] or [`MayDangle::unwrap`] before dereferencing the
422//! value. These types are `Copy`able.
423//!
424//! The [`Object::spawn`] method returns a `Strong` directly and handle newtypes can be upgraded
425//! into `Strong` references using the [`Handle::as_strong`] method.
426//!
427//! Objects are not destroyed immediately upon their reference count reaching zero. Instead, all
428//! deletions are queued until the [`World::flush`] method is called. This means that objects
429//! without any remaining strong references to them may be "resurrected" using the
430//! `Handle::as_strong` method like so:
431//!
432//! ```
433//! # use arid::World;
434//! #
435//! # let mut w = World::new();
436//! # let w = &mut w;
437//! #
438//! # use arid::object;
439//! #
440//! # #[derive(Debug)]
441//! # pub struct MyObject {
442//! # count: u32,
443//! # }
444//! #
445//! # object!(pub MyObject);
446//! use arid::{Handle as _, Object as _, Strong};
447//!
448//! let my_counter_strong: Strong<MyObjectHandle> = MyObject { count: 1 }.spawn(w);
449//! let my_counter_weak: MyObjectHandle = *my_counter_strong;
450//!
451//! // We still have a strong reference to our counter so nothing gets deleted.
452//! w.flush();
453//! assert!(my_counter_weak.is_alive(w));
454//!
455//! // We dropped the last remaining strong reference but re-created it with `as_strong`
456//! // before the next flush so the object is still alive.
457//! drop(my_counter_strong);
458//! assert!(my_counter_weak.is_alive(w));
459//! let my_counter_strong = my_counter_weak.as_strong(w);
460//! w.flush();
461//! assert!(my_counter_weak.is_alive(w));
462//!
463//! // Finally, we can drop the value!
464//! drop(my_counter_strong);
465//! assert!(my_counter_weak.is_alive(w));
466//! w.flush();
467//! assert!(!my_counter_weak.is_alive(w));
468//! ```
469//!
470//! You can define a custom destructor for a given object type by implementing the [`Destructor`]
471//! trait on its handle. This method is called during the call to `World::flush` immediately before
472//! the value is properly destroyed.
473//!
474//! ```
475//! # use arid::World;
476//! #
477//! # let mut w = World::new();
478//! # let w = &mut w;
479//! use arid::{Destructor, Handle, Object, object, W};
480//!
481//! #[derive(Debug)]
482//! pub struct DtorObserver {
483//! name: &'static str,
484//! }
485//!
486//! object!(pub DtorObserver);
487//!
488//! impl Destructor for DtorObserverHandle {
489//! fn pre_destroy(self, w: W) {
490//! println!("{} has been destroyed!", self.r(w).name);
491//! }
492//! }
493//!
494//! let object = DtorObserver { name: "Max" }.spawn(w);
495//!
496//! drop(object);
497//! w.flush(); // "Max has been destroyed!"
498//! ```
499//!
500//! # Polymorphism
501//!
502//! Handle newtypes are not limited to harboring inherent `impl` blocks. Indeed, they can also
503//! accommodate `dyn`-compatible `trait` implementations, providing a powerful mechanism for
504//! polymorphism.
505//!
506//! We'll begin by defining a new trait and making it inherit the [`ErasedHandle`] trait implemented
507//! by all [`Handle`]s.
508//!
509//! ```
510//! use arid::{ErasedHandle, W, Wr};
511//!
512//! pub trait AbstractCounter: ErasedHandle {
513//! fn increment(&self, w: W);
514//!
515//! fn get_count(&self, w: Wr) -> usize;
516//! }
517//! ```
518//!
519//! We can then implement that trait on a handle newtype.
520//!
521//! ```
522//! # use arid::{ErasedHandle, W, Wr};
523//! #
524//! # pub trait AbstractCounter: ErasedHandle {
525//! # fn increment(&self, w: W);
526//! #
527//! # fn get_count(&self, w: Wr) -> usize;
528//! # }
529//! use std::time::Instant;
530//!
531//! use arid::{object, Handle as _};
532//!
533//! #[derive(Debug, Default)]
534//! pub struct SimpleCounter {
535//! count: usize,
536//! }
537//!
538//! object!(pub SimpleCounter);
539//!
540//! impl AbstractCounter for SimpleCounterHandle {
541//! fn increment(&self, w: W) {
542//! self.m(w).count += 1;
543//! }
544//!
545//! fn get_count(&self, w: Wr) -> usize {
546//! self.r(w).count
547//! }
548//! }
549//!
550//! #[derive(Debug, Default)]
551//! pub struct ComplexCounter {
552//! records: Vec<Instant>,
553//! }
554//!
555//! object!(pub ComplexCounter);
556//!
557//! impl AbstractCounter for ComplexCounterHandle {
558//! fn increment(&self, w: W) {
559//! self.m(w).records.push(Instant::now());
560//! }
561//!
562//! fn get_count(&self, w: Wr) -> usize {
563//! self.r(w).records.len()
564//! }
565//! }
566//! ```
567//!
568//! While it is perfectly valid to store these `dyn Trait`-objects in a `Box`, it is likely much
569//! more ergonomic and performant to store them in an [`Erased`] handle wrapper, which is `Copy`able
570//! and comes with some useful helper methods for down-casting types. You can instantiate an
571//! `Erased` wrapper using the [`erase!`] macro like so:
572//!
573//! ```
574//! # use arid::{ErasedHandle, W, Wr};
575//! #
576//! # pub trait AbstractCounter: ErasedHandle {
577//! # fn increment(&self, w: W);
578//! #
579//! # fn get_count(&self, w: Wr) -> usize;
580//! # }
581//! # use std::time::Instant;
582//! #
583//! # use arid::{object, Handle as _};
584//! #
585//! # #[derive(Debug, Default)]
586//! # pub struct SimpleCounter {
587//! # count: usize,
588//! # }
589//! #
590//! # object!(pub SimpleCounter);
591//! #
592//! # impl AbstractCounter for SimpleCounterHandle {
593//! # fn increment(&self, w: W) {
594//! # self.m(w).count += 1;
595//! # }
596//! #
597//! # fn get_count(&self, w: Wr) -> usize {
598//! # self.r(w).count
599//! # }
600//! # }
601//! #
602//! # #[derive(Debug, Default)]
603//! # pub struct ComplexCounter {
604//! # records: Vec<Instant>,
605//! # }
606//! #
607//! # object!(pub ComplexCounter);
608//! #
609//! # impl AbstractCounter for ComplexCounterHandle {
610//! # fn increment(&self, w: W) {
611//! # self.m(w).records.push(Instant::now());
612//! # }
613//! #
614//! # fn get_count(&self, w: Wr) -> usize {
615//! # self.r(w).records.len()
616//! # }
617//! # }
618//! #
619//! # use arid::World;
620//! # let mut w = World::new();
621//! # let w = &mut w;
622//! use arid::{erase, Object as _};
623//!
624//! let simple_counter = SimpleCounter::default().spawn(w);
625//! let complex_counter = ComplexCounter::default().spawn(w);
626//!
627//! let mut counter = erase!(as dyn AbstractCounter, *simple_counter);
628//!
629//! counter.increment(w);
630//! assert_eq!(counter.get_count(w), 1);
631//! dbg!(counter.debug(w));
632//! assert!(counter.try_downcast::<SimpleCounterHandle>().is_some());
633//!
634//! counter = erase!(as dyn AbstractCounter, *complex_counter);
635//! counter.increment(w);
636//! assert_eq!(counter.get_count(w), 1);
637//! dbg!(counter.debug(w));
638//! assert!(counter.try_downcast::<ComplexCounterHandle>().is_some());
639//! ```
640//!
641//! ```text
642//! [poly.rs:59:5] counter.debug(w) = poly::main::SimpleCounter[0, v1]: SimpleCounter {
643//! count: 1,
644//! }
645//! [poly.rs:65:5] counter.debug(w) = poly::main::ComplexCounter[0, v1]: ComplexCounter {
646//! records: [
647//! Instant {
648//! tv_sec: 85959,
649//! tv_nsec: 910593500,
650//! },
651//! ],
652//! }
653//! ```
654//!
655//! [`Erased`] is a weak-but-assumed-valid handle to a value—sort of like a `Handle`. You can use
656//! [`StrongErased`] to create a strong reference to the value.
657//!
658//! # Custom Arenas
659//!
660//! To provide the user with copyable object handles, `arid` tracks its values in *generational
661//! arenas*, which can be thought of as very efficient dictionaries from the [`RawHandle`]s that
662//! [`Handle`]s wrap to the values to which they point. By default, we use the
663//! [`DefaultObjectArena`] arena but the user can provide a custom arena so long as it implements
664//! the [`ObjectArena`] trait. This could come in handy when trying to attach additional metadata to
665//! a variety of objects (e.g. widget parent and child relationships in a UI framework), when
666//! customizing those objects' deletion routine, or even when using an alternative data-structure to
667//! keep track of objects.
668//!
669//! This section will be reimplement the `DefaultObjectArena` structure in user-land but introduce
670//! additional metadata to it—in our case, a fancy new field named `frobs`! I expect this to be the
671//! most common way to define new arenas but this is only a pattern and, so long as you can properly
672//! implement the `ObjectArena` trait, you can do basically anything here.
673//!
674//! Let's start by defining creating a new-type structure to wrap a [`RawArena`]. Each slot actively
675//! allocated in the arena will have three fields: the actual value, its [`KeepAliveIndex`] so we
676//! can upgrade a given [`RawHandle`] to its corresponding [`KeepAlive`], and our custom metadata
677//! named `frobs`.
678//!
679//! The [`ObjectArena`] trait requires that our structure be [`Sized`], implement [`Default`], and
680//! live for `'static` so we'll derive those traits now.
681//!
682//! ```
683//! use arid::{RawArena, KeepAliveIndex, Object};
684//!
685//! #[derive(Debug)]
686//! pub struct MyArena<T: Object> {
687//! arena: RawArena<Slot<T>>,
688//! }
689//!
690//! impl<T: Object> Default for MyArena<T> {
691//! fn default() -> Self {
692//! Self {
693//! arena: RawArena::default(),
694//! }
695//! }
696//! }
697//!
698//! #[derive(Debug)]
699//! struct Slot<T: Object> {
700//! value: T,
701//! keep_alive: KeepAliveIndex,
702//! frobs: u32,
703//! }
704//! ```
705//!
706//! Now, let us implement the [`ObjectArena`] trait on the `MyArena` type for objects we wish to
707//! support. We can fetch a given arena instance using the [`World::arena`] and [`World::arena_mut`]
708//! methods and the [`KeepAliveManager`] used to track [`KeepAlive`]s using [`World::manager`],
709//! [`World::manager_mut`], and [`World::arena_and_manager_mut`].
710//!
711//! Here's some boilerplate for a minimal arena with very little customization:
712//!
713//! ```
714//! # use arid::{RawArena, KeepAliveIndex, Object};
715//! #
716//! # #[derive(Debug)]
717//! # pub struct MyArena<T: Object> {
718//! # arena: RawArena<Slot<T>>,
719//! # }
720//! #
721//! # impl<T: Object> Default for MyArena<T> {
722//! # fn default() -> Self {
723//! # Self {
724//! # arena: RawArena::default(),
725//! # }
726//! # }
727//! # }
728//! #
729//! # #[derive(Debug)]
730//! # struct Slot<T: Object> {
731//! # value: T,
732//! # keep_alive: KeepAliveIndex,
733//! # frobs: u32,
734//! # }
735//! use std::fmt;
736//!
737//! use arid::{Handle, ObjectArena, ObjectArenaSimpleSpawn, Strong, W, WorldKeepAliveUserdata, Wr};
738//!
739//! impl<T: Object<Arena = Self>> ObjectArenaSimpleSpawn for MyArena<T>
740//! where
741//! T: Object<Arena = Self> + fmt::Debug,
742//! {
743//! fn spawn(value: Self::Object, w: W) -> Strong<Self::Handle> {
744//! let (arena, manager) = w.arena_and_manager_mut::<Self>();
745//!
746//! // Add the value to the arena, obtaining a `RawHandle`.
747//! let handle = arena.arena.insert(Slot {
748//! value,
749//! // We'll initialize this after we allocate the slot's `KeepAlive` using the
750//! // `RawHandle` we allocate in the current step.
751//! keep_alive: KeepAliveIndex::MAX,
752//! frobs: 0,
753//! });
754//!
755//! // Create a `KeepAlive` to keep track of our slot.
756//! let keep_alive = manager.allocate(WorldKeepAliveUserdata {
757//! // The destructor is a function pointer that `World::flush` will call on objects
758//! // whose `KeepAlive`s have all expired.
759//! destructor: |handle, w| {
760//! let handle = Self::Handle::from_raw(handle);
761//!
762//! // The user is allowed to define custom destructors for their objects.
763//! // Don't forget to call them!
764//! Self::Handle::invoke_pre_destructor(handle, w);
765//!
766//! w.arena_mut::<Self>().arena.remove(handle.raw());
767//! },
768//! handle,
769//! });
770//!
771//! // Patch the temporary `keep_alive` in our slot with the new keep alive we just created.
772//! arena.arena.get_mut(handle).unwrap().keep_alive = keep_alive.index();
773//!
774//! // We now have a `RawHandle` and a `KeepAlive`, the two components required to create
775//! // the `Strong` handle the caller expects!
776//! Strong::new(Self::Handle::from_raw(handle), keep_alive)
777//! }
778//! }
779//!
780//! impl<T> ObjectArena for MyArena<T>
781//! where
782//! T: Object<Arena = Self> + fmt::Debug,
783//! {
784//! type Object = T;
785//! type Handle = T::Handle;
786//!
787//! fn try_get(handle: Self::Handle, w: Wr<'_>) -> Option<&Self::Object> {
788//! w.arena::<Self>().arena.get(handle.raw()).map(|v| &v.value)
789//! }
790//!
791//! fn try_get_mut(handle: Self::Handle, w: W<'_>) -> Option<&mut Self::Object> {
792//! w.arena_mut::<Self>().arena.get_mut(handle.raw()).map(|v| &mut v.value)
793//! }
794//!
795//! fn as_strong_if_alive(handle: Self::Handle, w: W) -> Option<Strong<Self::Handle>> {
796//! let (arena, manager) = w.arena_and_manager_mut::<Self>();
797//!
798//! // Ensure the handle is still alive.
799//! let slot = arena.arena.get(handle.raw())?;
800//!
801//! // If it is, upgrade its `KeepAliveIndex` to a `KeepAlive` guard.
802//! let keep_alive = manager.upgrade(slot.keep_alive);
803//!
804//! Some(Strong::new(handle, keep_alive))
805//! }
806//!
807//! fn print_debug(f: &mut fmt::Formatter<'_>, handle: Self::Handle, w: Wr) -> fmt::Result {
808//! if let Some(alive) = handle.try_r(w) {
809//! alive.fmt(f)
810//! } else {
811//! f.write_str("<dangling>")
812//! }
813//! }
814//! }
815//! ```
816//!
817//! Finally, let's create extension traits to define new methods on objects within our new arena.
818//!
819//! ```
820//! # use arid::{RawArena, KeepAliveIndex, Object};
821//! #
822//! # #[derive(Debug)]
823//! # pub struct MyArena<T: Object> {
824//! # arena: RawArena<Slot<T>>,
825//! # }
826//! #
827//! # impl<T: Object> Default for MyArena<T> {
828//! # fn default() -> Self {
829//! # Self {
830//! # arena: RawArena::default(),
831//! # }
832//! # }
833//! # }
834//! #
835//! # #[derive(Debug)]
836//! # struct Slot<T: Object> {
837//! # value: T,
838//! # keep_alive: KeepAliveIndex,
839//! # frobs: u32,
840//! # }
841//! # use std::fmt;
842//! #
843//! # use arid::{Handle, ObjectArena, ObjectArenaSimpleSpawn, Strong, W, WorldKeepAliveUserdata, Wr};
844//! #
845//! # impl<T: Object<Arena = Self>> ObjectArenaSimpleSpawn for MyArena<T>
846//! # where
847//! # T: Object<Arena = Self> + fmt::Debug,
848//! # {
849//! # fn spawn(value: Self::Object, w: W) -> Strong<Self::Handle> {
850//! # let (arena, manager) = w.arena_and_manager_mut::<Self>();
851//! #
852//! # // Add the value to the arena, obtaining a `RawHandle`.
853//! # let handle = arena.arena.insert(Slot {
854//! # value,
855//! # // We'll initialize this after we allocate the slot's `KeepAlive` using the
856//! # // `RawHandle` we allocate in the current step.
857//! # keep_alive: KeepAliveIndex::MAX,
858//! # frobs: 0,
859//! # });
860//! #
861//! # // Create a `KeepAlive` to keep track of our slot.
862//! # let keep_alive = manager.allocate(WorldKeepAliveUserdata {
863//! # // The destructor is a function pointer that `World::flush` will call on objects
864//! # // whose `KeepAlive`s have all expired.
865//! # destructor: |handle, w| {
866//! # let handle = Self::Handle::from_raw(handle);
867//! #
868//! # // The user is allowed to define custom destructors for their objects.
869//! # // Don't forget to call them!
870//! # Self::Handle::invoke_pre_destructor(handle, w);
871//! #
872//! # w.arena_mut::<Self>().arena.remove(handle.raw());
873//! # },
874//! # handle,
875//! # });
876//! #
877//! # // Patch the temporary `keep_alive` in our slot with the new keep alive we just created.
878//! # arena.arena.get_mut(handle).unwrap().keep_alive = keep_alive.index();
879//! #
880//! # // We now have a `RawHandle` and a `KeepAlive`, the two components required to create
881//! # // the `Strong` handle the caller expects!
882//! # Strong::new(Self::Handle::from_raw(handle), keep_alive)
883//! # }
884//! # }
885//! #
886//! # impl<T> ObjectArena for MyArena<T>
887//! # where
888//! # T: Object<Arena = Self> + fmt::Debug,
889//! # {
890//! # type Object = T;
891//! # type Handle = T::Handle;
892//! #
893//! # fn try_get(handle: Self::Handle, w: Wr<'_>) -> Option<&Self::Object> {
894//! # w.arena::<Self>().arena.get(handle.raw()).map(|v| &v.value)
895//! # }
896//! #
897//! # fn try_get_mut(handle: Self::Handle, w: W<'_>) -> Option<&mut Self::Object> {
898//! # w.arena_mut::<Self>().arena.get_mut(handle.raw()).map(|v| &mut v.value)
899//! # }
900//! #
901//! # fn as_strong_if_alive(handle: Self::Handle, w: W) -> Option<Strong<Self::Handle>> {
902//! # let (arena, manager) = w.arena_and_manager_mut::<Self>();
903//! #
904//! # // Ensure the handle is still alive.
905//! # let slot = arena.arena.get(handle.raw())?;
906//! #
907//! # // If it is, upgrade its `KeepAliveIndex` to a `KeepAlive` guard.
908//! # let keep_alive = manager.upgrade(slot.keep_alive);
909//! #
910//! # Some(Strong::new(handle, keep_alive))
911//! # }
912//! #
913//! # fn print_debug(f: &mut fmt::Formatter<'_>, handle: Self::Handle, w: Wr) -> fmt::Result {
914//! # if let Some(alive) = handle.try_r(w) {
915//! # alive.fmt(f)
916//! # } else {
917//! # f.write_str("<dangling>")
918//! # }
919//! # }
920//! # }
921//! pub trait MyObject: Object<Arena = MyArena<Self>> + fmt::Debug {}
922//!
923//! impl<T: Object<Arena = MyArena<Self>> + fmt::Debug> MyObject for T {}
924//!
925//! pub trait MyHandle: Handle<Object: MyObject> {
926//! fn frob(self, w: W);
927//!
928//! fn get_frobs(self, w: Wr) -> u32;
929//! }
930//!
931//! impl<T: Handle<Object: MyObject>> MyHandle for T {
932//! fn frob(self, w: W) {
933//! w.arena_mut::<MyArena<T::Object>>()
934//! .arena
935//! .get_mut(self.raw())
936//! .expect("value is not alive")
937//! .frobs += 1;
938//! }
939//!
940//! fn get_frobs(self, w: Wr) -> u32 {
941//! w.arena::<MyArena<T::Object>>()
942//! .arena
943//! .get(self.raw())
944//! .expect("value is not alive")
945//! .frobs
946//! }
947//! }
948//! ```
949//!
950//! And with that, we have a working arena; now, we just need to use it! To define an object within
951//! the arena, we can use a second form of the [`object!`] macro to specify a custom arena in which
952//! the object should be stored:
953//!
954//! ```
955//! # use arid::{RawArena, KeepAliveIndex, Object};
956//! #
957//! # #[derive(Debug)]
958//! # pub struct MyArena<T: Object> {
959//! # arena: RawArena<Slot<T>>,
960//! # }
961//! #
962//! # impl<T: Object> Default for MyArena<T> {
963//! # fn default() -> Self {
964//! # Self {
965//! # arena: RawArena::default(),
966//! # }
967//! # }
968//! # }
969//! #
970//! # #[derive(Debug)]
971//! # struct Slot<T: Object> {
972//! # value: T,
973//! # keep_alive: KeepAliveIndex,
974//! # frobs: u32,
975//! # }
976//! # use std::fmt;
977//! #
978//! # use arid::{Handle, ObjectArena, ObjectArenaSimpleSpawn, Strong, W, WorldKeepAliveUserdata, Wr};
979//! #
980//! # impl<T: Object<Arena = Self>> ObjectArenaSimpleSpawn for MyArena<T>
981//! # where
982//! # T: Object<Arena = Self> + fmt::Debug,
983//! # {
984//! # fn spawn(value: Self::Object, w: W) -> Strong<Self::Handle> {
985//! # let (arena, manager) = w.arena_and_manager_mut::<Self>();
986//! #
987//! # // Add the value to the arena, obtaining a `RawHandle`.
988//! # let handle = arena.arena.insert(Slot {
989//! # value,
990//! # // We'll initialize this after we allocate the slot's `KeepAlive` using the
991//! # // `RawHandle` we allocate in the current step.
992//! # keep_alive: KeepAliveIndex::MAX,
993//! # frobs: 0,
994//! # });
995//! #
996//! # // Create a `KeepAlive` to keep track of our slot.
997//! # let keep_alive = manager.allocate(WorldKeepAliveUserdata {
998//! # // The destructor is a function pointer that `World::flush` will call on objects
999//! # // whose `KeepAlive`s have all expired.
1000//! # destructor: |handle, w| {
1001//! # let handle = Self::Handle::from_raw(handle);
1002//! #
1003//! # // The user is allowed to define custom destructors for their objects.
1004//! # // Don't forget to call them!
1005//! # Self::Handle::invoke_pre_destructor(handle, w);
1006//! #
1007//! # w.arena_mut::<Self>().arena.remove(handle.raw());
1008//! # },
1009//! # handle,
1010//! # });
1011//! #
1012//! # // Patch the temporary `keep_alive` in our slot with the new keep alive we just created.
1013//! # arena.arena.get_mut(handle).unwrap().keep_alive = keep_alive.index();
1014//! #
1015//! # // We now have a `RawHandle` and a `KeepAlive`, the two components required to create
1016//! # // the `Strong` handle the caller expects!
1017//! # Strong::new(Self::Handle::from_raw(handle), keep_alive)
1018//! # }
1019//! # }
1020//! #
1021//! # impl<T> ObjectArena for MyArena<T>
1022//! # where
1023//! # T: Object<Arena = Self> + fmt::Debug,
1024//! # {
1025//! # type Object = T;
1026//! # type Handle = T::Handle;
1027//! #
1028//! # fn try_get(handle: Self::Handle, w: Wr<'_>) -> Option<&Self::Object> {
1029//! # w.arena::<Self>().arena.get(handle.raw()).map(|v| &v.value)
1030//! # }
1031//! #
1032//! # fn try_get_mut(handle: Self::Handle, w: W<'_>) -> Option<&mut Self::Object> {
1033//! # w.arena_mut::<Self>().arena.get_mut(handle.raw()).map(|v| &mut v.value)
1034//! # }
1035//! #
1036//! # fn as_strong_if_alive(handle: Self::Handle, w: W) -> Option<Strong<Self::Handle>> {
1037//! # let (arena, manager) = w.arena_and_manager_mut::<Self>();
1038//! #
1039//! # // Ensure the handle is still alive.
1040//! # let slot = arena.arena.get(handle.raw())?;
1041//! #
1042//! # // If it is, upgrade its `KeepAliveIndex` to a `KeepAlive` guard.
1043//! # let keep_alive = manager.upgrade(slot.keep_alive);
1044//! #
1045//! # Some(Strong::new(handle, keep_alive))
1046//! # }
1047//! #
1048//! # fn print_debug(f: &mut fmt::Formatter<'_>, handle: Self::Handle, w: Wr) -> fmt::Result {
1049//! # if let Some(alive) = handle.try_r(w) {
1050//! # alive.fmt(f)
1051//! # } else {
1052//! # f.write_str("<dangling>")
1053//! # }
1054//! # }
1055//! # }
1056//! # pub trait MyObject: Object<Arena = MyArena<Self>> + fmt::Debug {}
1057//! #
1058//! # impl<T: Object<Arena = MyArena<Self>> + fmt::Debug> MyObject for T {}
1059//! #
1060//! # pub trait MyHandle: Handle<Object: MyObject> {
1061//! # fn frob(self, w: W);
1062//! #
1063//! # fn get_frobs(self, w: Wr) -> u32;
1064//! # }
1065//! #
1066//! # impl<T: Handle<Object: MyObject>> MyHandle for T {
1067//! # fn frob(self, w: W) {
1068//! # w.arena_mut::<MyArena<T::Object>>()
1069//! # .arena
1070//! # .get_mut(self.raw())
1071//! # .expect("value is not alive")
1072//! # .frobs += 1;
1073//! # }
1074//! #
1075//! # fn get_frobs(self, w: Wr) -> u32 {
1076//! # w.arena::<MyArena<T::Object>>()
1077//! # .arena
1078//! # .get(self.raw())
1079//! # .expect("value is not alive")
1080//! # .frobs
1081//! # }
1082//! # }
1083//! #
1084//! # use arid::World;
1085//! # let mut w = World::new();
1086//! # let w = &mut w;
1087//! use arid::{object, Object as _};
1088//!
1089//! #[derive(Debug)]
1090//! pub struct MyThing {
1091//! name: &'static str,
1092//! }
1093//!
1094//! object!(pub MyThing[MyArena<Self>]);
1095//!
1096//! let my_thing = MyThing { name: "Ryleigh" }.spawn(w);
1097//!
1098//! my_thing.frob(w);
1099//! my_thing.m(w).name = "Riley";
1100//! assert_eq!(my_thing.get_frobs(w), 1);
1101//! ```
1102//!
1103//! Happy hacking!
1104//!
1105//! # Limitations and Future Work
1106//!
1107//! `arid`'s largest limitation is its lack of support for generic [`Object`] definitions. This
1108//! limitation originates from our use of [`late_struct`] to build up our [`World`]s: each object
1109//! declaration defines a new field in our world and these late-bound field definitions cannot be
1110//! generic.
1111//!
1112//! Our use of `late_struct` also means that the size of each world is proportional to the number of
1113//! objects defined in the binary but the constant factor on this size is, intentionally, fairly
1114//! small (currently, 32 bytes per arena type).
1115//!
1116//! The use of arenas to implement this object model is also somewhat unfortunate. Arenas transform
1117//! the (small) cost of incrementing and decrementing reference counts into the (equally small) cost
1118//! of checking object generations before accessing a handle. This is a somewhat suspicious tradeoff
1119//! since I'd expect dereferences to happen more often than reference copies but that's just a
1120//! hunch.
1121//!
1122//! Really, arenas are just a work-around to give us `Copy`able handles for ergonomic purposes.
1123//! Hopefully, this need for arenas will be obviated by the [ergonomic ref-counting] team's efforts.
1124//! Likewise, the handle newtype system is really just a work-around for the lack of [arbitrary
1125//! `Self` types](https://github.com/rust-lang/rust/issues/44874).
1126//!
1127//! [ergonomic ref-counting]: https://rust-lang.github.io/rust-project-goals/2024h2/ergonomic-rc.html
1128//!
1129
1130#![deny(missing_docs)]
1131
1132mod arena;
1133pub use self::arena::*;
1134
1135mod keep_alive;
1136pub use self::keep_alive::*;
1137
1138mod handle;
1139pub use self::handle::*;
1140
1141mod world;
1142pub use self::world::*;
1143
1144mod wrappers;
1145pub use self::wrappers::*;