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.
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.
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.