Using the type system to enforce that a key is used on the same object that created it?

I have an API that looks something roughly like this:

struct Foo {}
impl Foo {
    pub fn create_key(&self) -> Key;
    pub fn use_key(&self, key: &Key); // key MUST have been created by &self
}

struct Key {} // Contents elided in demo code

The Keys will only ever need to exist locally on the stack, ie

let key = foo.create_key();
// ... a couple more lines of code here
foo.use_key(key); // this call can happen multiple times

Is there a way to have the "key must be created by &self" constraint enforced by the type system? I currently do this using runtime checks that I can usually - but not always - persuade the compiler to remove, but it would be significantly neater if this were statically enforced.

This is sort of to do with ownership, so maybe there's a trick that can come out of framing it as "key must be owned by foo" or something like that?

I'd suggest a closure-based API, probably. At a glance, this looks a lot like GhostCell, with GhostToken as Key.

6 Likes

I'm just reading through the GhostCell paper now, but it looks like it's along the right lines, thanks! I think "branded types" using lifetimes was the idea that I had in my head but didn't know how to realise. If they're as capable of a tool as they look, this is going to be a fun toy!

1 Like

qcell::LCell is another example of using unique invariant lifetimes as a marker of object identity (also with a closure-based interface).

2 Likes

I'm still designing the details of my API, but branded types and invariant lifetimes as used in GhostCell have unlocked my design. Thanks @Cerber-Ursi.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.