The example as written fails to compile when we uncomment the code:
fn main() {
with_arena(|arena1| {
let handle1 = arena1.alloc("hello".to_string());
println!("{}", arena1.get(&handle1)); // ✅
// Can't use handle1 with a different arena — compile-time error
with_arena(|arena2| {
arena2.get(&handle1); // ❌ borrowed data escapes outside of closure
});
});
}
If we rearrange the code a bit:
fn main() {
with_arena(|arena1| {
// Can't use handle1 with a different arena — compile-time error
with_arena(|arena2| {
let handle1 = arena1.alloc("hello".to_string());
println!("{}", arena1.get(&handle1)); // ✅
arena2.get(&handle1); // ❌ borrowed data escapes outside of closure
});
});
}
This compiles - even though it isn't supposed to.
By moving the brand to the Arena instead of keeping it on the ArenaHandle it works as intended:
/// A handle branded to a specific arena instance.
/// Invariant over 'arena — prevents using a handle from one arena with another.
struct ArenaHandle<'arena> {
index: usize,
_phantom: PhantomData<&'arena ()>,
}
/// An arena that brands each handle with its unique lifetime.
struct Arena<'arena> {
data: RefCell<Vec<String>>,
_brand: PhantomData<*mut &'arena ()>,
}
RustTraining/rust-patterns-book/src/ch04-phantomdata-types-that-carry-no-data.md
Line 44 in 278f1d7
The example as written fails to compile when we uncomment the code:
If we rearrange the code a bit:
This compiles - even though it isn't supposed to.
By moving the brand to the
Arenainstead of keeping it on theArenaHandleit works as intended: