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);
}

Tags:

Rust