Code Structure: Manage Common Context

I am currently facing a design challenge as below:

struct Context {}

struct Queue<'a> { ctx: Context, marker: PhantomMarker<&'a Element> }

struct Element { ctx: Context }

impl Context {
    fn queue(&self) -> Queue;
    fn element(&self, val: i32) -> Element;
}

impl<'a> Queue<'a> {
    fn push(&self, element: &'a element);
}

fn test() {
    let ctx = Context::new();
    let q1 = Queue::new(&ctx);
    let q2 = Queue::new(&ctx);
    let e1 = Element::new(&ctx);
    let e2 = Element::new(&ctx);
    q1.push(&e1);
    q1.push(&e2);
    q2.push(&e2);
}

The problem is that I want every Queue andElement interacting to have the same underlying Context, but I am having a hard time enforcing this constraint in the design level.

Are there any suggestions?

In my experience, It's nearly impossible to express constraints like this statically (in any language, not just Rust). If you try to rearrange the interface so that "wrong context" can't be expressed, you'll just end up creating a need for a different run-time check, like "is this element-handle actually valid".

Just have Queue::push(), and anything else that brings together two entities from the system, check that they belong to the same context.

Use unique ID objects if you need to — just create a struct that wraps a u64 you got from incrementing an AtomicU64. That's all you need for IDs within a single process.

1 Like

There is actually a way to express this and that's to use HRTBs and invariant lifetimes like ghostcell do. This however imposed pretty strong restrictions on the UX though.