How can I compare objects only when they are created from the same parent object?
I'll happily explain the lifetime approach, but it doesn't seem to be viable.
When is X
a subtype of Y
(denoted X <: Y
)?
The question posed for generics involves variance.
The variance answers the question: for a generic type G<X>
, what does X <: Y
mean for the relation of G<X>
to G<Y>
.
- Covariance:
X <: Y
=>G<X> <: G<Y>
- Invariance:
X == Y
=>G<X> <: G<Y>
- Contravariance:
X <: Y
=>G<Y> <: G<X>
Cell<X>
is invariant w.r.t X
, so phantom: PhantomData<Cell<&'a Parent>>,
makes Child<'a>
invariant w.r.t 'a
.
PhantomData
is a way to trick you to talk about variance by just describing it in types you already know.
This appears to work, but not so fast, because we can create a situation where the lifetimes are totally equal, and then the test compiles again!
let (parent, parent2) = (Parent::new(1), Parent::new(1));
let (child, child2) = (parent.child(2), parent2.child(2));
// Plan is foiled!!
Here's an answer that could excite you or scare you to death: Use monads.
Specifically the ST Monad. Unfortunately, I can't explain it to you, but it is my understanding that it could be useful in this case. Let us know if you figure it out!
I was shown this for my own project:
You can replicate InvariantLifetime, which is what is actually done in the BTreeMap implementation. The reason I say it's very heavyweight is that the only way to make its properties useful is to require each document to be created (or at least, accessed) within a closure.
The pragmatic solution is to not attempt to make these compile-time errors, but just include the parent pointer in the comparison:
#[derive(Debug,Copy,Clone,PartialEq)]
struct Parent {
val: u64,
}
impl Parent {
fn child(&self) -> Child { Child { parent: self, val: self.val } }
}
#[derive(Debug,Copy,Clone)]
struct Child<'a> {
parent: &'a Parent,
val: u64,
}
impl<'a> PartialEq for Child<'a> {
fn eq(&self, other: &Child<'a>) -> bool {
(self.parent as *const _, self.val) == (other.parent as *const _, other.val)
}
}
fn main() {
let (p1, p2) = (Parent { val: 42 }, Parent { val: 42 });
let p1_c1 = p1.child();
let p1_c2 = p1.child();
let p2_c1 = p2.child();
println!("{}", p1_c1 == p1_c2);
println!("{}", p1_c1 == p2_c1);
}