I am trying to create a system where objects are owned by an Arena
and you can hold handles to them. This lets me create non-borrow checker friendly structures that I need (like graphs). This makes sure that when an Arena
goes out of scope there are no references to it's data and it is safe to drop. I have a trait IntoObject
that allocates and object and returns a handle.
What I want is to be able to define IntoObject
on a Handle
and it be essentially a no-op. But I want the compiler to statically verify that the new handle cannot outlive the old handle. I was trying to use Lifetime Subtyping to enforce this, but I am getting compiler messages saying that it can't prove that the lifetimes work. I need help deciphering the error and doing this the correct way.
use std::marker::PhantomData;
pub enum Object {
Int(i64),
Float(f64),
}
#[derive(Copy, Clone)]
pub struct Handle<'a> {
ptr: *const Object,
marker: PhantomData<&'a ()>,
}
pub struct Arena {
// not shown
}
impl Arena {
fn alloc(&self, obj: Object) -> Handle {
// real implentation not shown
Handle {
ptr: Box::into_raw(Box::new(obj)),
marker: PhantomData,
}
}
}
pub trait IntoObject {
fn into_object(self, arena: &Arena) -> Handle;
}
impl IntoObject for i64 {
fn into_object(self, arena: &Arena) -> Handle {
arena.alloc(Object::Int(self))
}
}
impl IntoObject for f64 {
fn into_object(self, arena: &Arena) -> Handle {
arena.alloc(Object::Float(self))
}
}
// This should be a no-op
impl<'a, 'b: 'a> IntoObject for Handle<'a> {
fn into_object(self, arena: &'b Arena) -> Handle<'b> {
self
}
}
fn main() {
let long_arena = Arena{};
let long_handle = 10.into_object(&long_arena);
{
let short_arena = Arena{};
let short_handle = 3.3.into_object(&short_arena);
// This should work
let from_handle = long_handle.into_object(&short_arena);
}
}