Lifetime-based Symbol API

We often have the following designs:

struct Context {}

struct Resource {}

impl Context {
    pub fn new() -> Self {
        Self {}
    }

    pub fn create_resource(&self) -> Resource {
        todo!()
    }

    pub fn delete_resource(&self, res: Resource) {
        // Check the context of the resource
        todo!()
    }
}

However, our users may create multiple 'Context', so we have to check owner of Resource at runtime.

Therefore, I try to avoid it by following the design:

pub type Invariant<'s> = PhantomData<*mut &'s ()>;

pub struct Symbol<'s>(Invariant<'s>);

pub fn for_symbol0<F: FnOnce() -> R, R>(f: F) -> R {
    f()
}

pub fn for_symbol1<F: for<'s1> FnOnce(Symbol<'s1>) -> R, R>(f: F) -> R {
    f(Symbol(PhantomData))
}

pub fn for_symbol2<F: for<'s1, 's2> FnOnce(Symbol<'s1>, Symbol<'s2>) -> R, R>(f: F) -> R {
    f(Symbol(PhantomData), Symbol(PhantomData))
}

Only a few changes by the designer are required:

struct NewContext<'c> {
    lifetime: Symbol<'c>,
}

struct NewResource<'c> {
    p: Invariant<'c>,
}

impl<'c> NewContext<'c> {
    pub fn new(lifetime: Symbol<'c>) -> Self {
        Self {
            lifetime
        }
    }

    pub fn create_resource(&self) -> NewResource<'c> {
        todo!()
    }

    pub fn delete_resource(&self, res: NewResource<'c>) {
        todo!()
    }
}

fn main() {
    for_symbol2(|l1, l2| {
        let c1 = NewContext::new(l1);
        let c2 = NewContext::new(l2);
        let res = c1.create_resource();

        // compile success
        c1.delete_resource(res);

        // compile fail
        // c2.delete_resource(res);
    });
}

Questions unrelated to the design of Rust, its tooling, or its standard library are more appropriately asked on users.rust-lang.org :wink:

For existing crates offering a way to create “unique” invariant lifetime parameters, take a look at generativity - Rust

3 Likes