alloc_once/lib.rs
1//! `alloc_once!` — allocate several different-typed values in a single bump
2//! allocation and get back a guard that owns the arena.
3//!
4//! This crate is a thin facade over [`bumpalo`]: it re-exports the [`alloc_once`]
5//! procedural macro and provides the [`AllocOnce`] guard the macro expands into.
6//! Callers never name bumpalo themselves.
7//!
8//! ```
9//! use alloc_once::alloc_once;
10//!
11//! let mut guard = alloc_once!(1i32, [10u8, 20, 30], "hello");
12//! let (n, bytes, s) = &mut *guard; // independent &mut to each field
13//! *n += 41;
14//! bytes[0] = 99;
15//! assert_eq!(*n, 42);
16//! assert_eq!(bytes[0], 99);
17//! assert_eq!(*s, "hello");
18//! // `guard` drops here: it runs the values' destructors, then frees the arena.
19//! ```
20
21// The `alloc_once!` expansion refers to this crate by its absolute path
22// (`::alloc_once::...`). That resolves fine in downstream crates, but inside this
23// crate itself the name isn't in the extern prelude, so alias the crate to itself
24// to make the macro usable in our own doctests and unit tests.
25extern crate self as alloc_once;
26
27pub use alloc_once_macros::alloc_once;
28
29/// Implementation details referenced by the [`alloc_once!`] expansion.
30///
31/// Not part of the public API — do not use directly.
32#[doc(hidden)]
33pub mod __private {
34 pub use bumpalo::Bump;
35}
36
37/// Owns a bump arena and the single value allocated inside it.
38///
39/// Access the value through `Deref`/`DerefMut` (`&*guard` / `&mut *guard`). When
40/// the guard is dropped it runs the value's destructor and then frees the arena —
41/// unlike a raw `bumpalo::Bump::alloc`, whose values are never dropped.
42///
43/// Construct one with the [`alloc_once!`] macro rather than by hand.
44pub struct AllocOnce<T> {
45 // `ptr` points into a heap chunk owned by `arena`. A `Bump`'s chunks live on
46 // the heap, so moving the `Bump` value into this struct does not move the
47 // chunk memory, and the pointer stays valid for as long as the guard lives.
48 //
49 // Field order matters: `ptr` is listed first so that, combined with the
50 // manual `Drop` below, the value is dropped before `arena` frees its memory.
51 ptr: *mut T,
52 // Held only to keep the backing memory alive and to free it on drop; never
53 // read directly, hence the allow.
54 #[allow(dead_code)]
55 arena: bumpalo::Bump,
56}
57
58impl<T> AllocOnce<T> {
59 /// Build a guard from an arena and a pointer to a value allocated inside it.
60 ///
61 /// # Safety
62 ///
63 /// `ptr` must point to a valid, initialized `T` that was allocated in
64 /// `arena` and is uniquely owned by the returned guard. The [`alloc_once!`]
65 /// macro is the only intended caller.
66 #[doc(hidden)]
67 pub unsafe fn __from_parts(arena: bumpalo::Bump, ptr: *mut T) -> Self {
68 Self { ptr, arena }
69 }
70}
71
72impl<T> core::ops::Deref for AllocOnce<T> {
73 type Target = T;
74
75 fn deref(&self) -> &T {
76 // SAFETY: `ptr` is valid for the guard's lifetime and uniquely owned.
77 unsafe { &*self.ptr }
78 }
79}
80
81impl<T> core::ops::DerefMut for AllocOnce<T> {
82 fn deref_mut(&mut self) -> &mut T {
83 // SAFETY: `&mut self` guarantees unique access to the owned value.
84 unsafe { &mut *self.ptr }
85 }
86}
87
88impl<T> Drop for AllocOnce<T> {
89 fn drop(&mut self) {
90 // SAFETY: the guard is the sole owner of the value, so dropping it here
91 // is sound and cannot double-drop. `arena` is dropped right after this
92 // body runs, freeing the chunk the value lived in.
93 unsafe { core::ptr::drop_in_place(self.ptr) }
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use crate::alloc_once;
100 use std::cell::Cell;
101 use std::rc::Rc;
102
103 #[test]
104 fn single_value() {
105 let mut g = alloc_once!(1u8);
106 let (only,) = &mut *g;
107 *only += 1;
108 assert_eq!(*only, 2);
109 }
110
111 #[test]
112 fn many_different_types() {
113 let mut g = alloc_once!(1i32, [10u8, 20, 30], "hello");
114 let (n, bytes, s) = &mut *g;
115 *n += 41;
116 bytes[0] = 99;
117 assert_eq!((*n, bytes[0], *s), (42, 99, "hello"));
118 }
119
120 #[test]
121 fn drops_value_when_guard_drops() {
122 // A value whose Drop flips a shared flag, proving the guard runs
123 // destructors (a raw bump allocation would not).
124 struct Flag(Rc<Cell<bool>>);
125 impl Drop for Flag {
126 fn drop(&mut self) {
127 self.0.set(true);
128 }
129 }
130
131 let dropped = Rc::new(Cell::new(false));
132 let guard = alloc_once!(Flag(Rc::clone(&dropped)));
133 assert!(!dropped.get());
134 drop(guard);
135 assert!(dropped.get());
136 }
137}