@@ -49,45 +49,55 @@ Use `PhantomData` to prevent mixing values from different "sessions" or "context
4949use std :: cell :: RefCell ;
5050use std :: marker :: PhantomData ;
5151
52- /// A handle that's valid only within a specific arena's lifetime
52+ /// A handle branded to a specific arena instance.
53+ /// Invariant over 'arena — prevents using a handle from one arena with another.
5354struct ArenaHandle <'arena > {
5455 index : usize ,
55- _brand : PhantomData <& 'arena ()>,
56+ _brand : PhantomData <* mut & 'arena ()>,
5657}
5758
58- struct Arena {
59+ /// An arena that brands each handle with its unique lifetime.
60+ struct Arena <'arena > {
5961 data : RefCell <Vec <String >>,
62+ _phantom : PhantomData <& 'arena ()>,
6063}
6164
62- impl Arena {
63- fn new () -> Self {
64- Arena { data : RefCell :: new (Vec :: new ()) }
65- }
65+ /// Create an arena and pass it to a closure.
66+ /// Each call gets a unique, opaque lifetime that can't be forged.
67+ fn with_arena <R >(f : impl for <'arena > FnOnce (& Arena <'arena >) -> R ) -> R {
68+ let arena = Arena {
69+ data : RefCell :: new (Vec :: new ()),
70+ _phantom : PhantomData ,
71+ };
72+ f (& arena )
73+ }
6674
75+ impl <'arena > Arena <'arena > {
6776 /// Allocate a string and return a branded handle
68- fn alloc (& self , value : String ) -> ArenaHandle <'_ > {
77+ fn alloc (& self , value : String ) -> ArenaHandle <'arena > {
6978 let mut data = self . data. borrow_mut ();
7079 let index = data . len ();
7180 data . push (value );
7281 ArenaHandle { index , _brand : PhantomData }
7382 }
7483
7584 /// Look up by handle — only accepts handles from THIS arena
76- fn get <' a >( & ' a self , handle : ArenaHandle <'a >) -> String {
85+ fn get ( & self , handle : & ArenaHandle <'arena >) -> String {
7786 let data = self . data. borrow ();
7887 data [handle . index]. clone ()
7988 }
8089}
8190
8291fn main () {
83- let arena1 = Arena :: new ();
84- let handle1 = arena1 . alloc (" hello" . to_string ());
85-
86- // Can't use handle1 with a different arena — lifetimes won't match
87- // let arena2 = Arena::new();
88- // arena2.get(handle1); // ❌ Lifetime mismatch
89-
90- println! (" {}" , arena1 . get (handle1 )); // ✅
92+ with_arena (| arena1 | {
93+ let handle1 = arena1 . alloc (" hello" . to_string ());
94+ println! (" {}" , arena1 . get (& handle1 )); // ✅
95+
96+ // Can't use handle1 with a different arena — compile-time error
97+ // with_arena(|arena2| {
98+ // arena2.get(&handle1); // ❌ borrowed data escapes outside of closure
99+ // });
100+ });
91101}
92102```
93103
0 commit comments