如何仅在从同一父对象创建对象时才比较对象?

时间:2015-05-29 12:41:14

标签: rust

我想比较Obj仅当它们是从同一个Parent对象创建的时候才有效,但它只有一种方式:如果你切换比较顺序,它就不会。 / p>

这是最小的代码:

use std::marker::PhantomData;

struct Parent {
    val: u64,
}

impl Parent {
    pub fn new(v: u64) -> Parent {
        Parent { val: v }
    }

    pub fn child(&self, v: u64) -> Child {
        Child {
            val: v,
            phantom: PhantomData,
        }
    }
}

struct Child<'a> {
    val: u64,
    phantom: PhantomData<&'a Parent>,
}

impl<'a> Child<'a> {
    pub fn compare(&'a self, l: &Obj<'a>, r: &Obj<'a>) -> bool {
        l.val == r.val
    }

    pub fn obj(&'a self, v: u64) -> Obj<'a> {
        Obj {
            val: v,
            child: self,
        }
    }
}

struct Obj<'a> {
    val: u64,
    child: &'a Child<'a>,
}

impl<'a> PartialEq<Obj<'a>> for Obj<'a> {
    fn eq(&self, other: &Obj<'a>) -> bool {
        self.child.compare(self, other)
    }
}

#[test]
fn test() {
    let parent = Parent::new(1);
    let child = parent.child(2);
    let obj1 = child.obj(3);
    let obj2 = child.obj(3);

    // those are from the same parent, this sould work (and works).
    assert!(obj1 == obj2);
    assert!(obj2 == obj1);

    let parent2 = Parent::new(1);
    let child2 = parent2.child(2);
    let obj12 = child2.obj(3);
    let obj22 = child2.obj(3);

    // this works fine too
    assert!(obj12 == obj22);
    assert!(obj22 == obj12);

    // those are from different parents

    //assert!(obj1 == obj12); // that line DOES NOT compile, which is exactly what I want!
    assert!(obj12 == obj1); // but that line suddenly DOES compile.
}

如何更改代码以便最后一行不会编译?

2 个答案:

答案 0 :(得分:3)

我很乐意解释一生的方法,但它似乎并不可行。

X何时Y的子类型(表示为X <: Y)?

针对泛型的问题涉及方差

方差回答了问题:对于通用类型G<X>X <: YG<X>G<Y>的关系意味着什么。

  • 协方差:X <: Y =&gt; G<X> <: G<Y>
  • 不变性:X == Y =&gt; G<X> <: G<Y>
  • 逆变法:X <: Y =&gt; G<Y> <: G<X>

Cell<X>X不相同,因此phantom: PhantomData<Cell<&'a Parent>>,使Child<'a> 不变 w.r.t 'a

PhantomData是一种通过仅以您已知的类型描述方式来诱骗您谈论差异的方法。

这似乎有效,但不是那么快,因为我们可以创建一个生命周期完全相等的情况,然后测试再次编译!

let (parent, parent2) = (Parent::new(1), Parent::new(1));
let (child, child2) = (parent.child(2), parent2.child(2));
// Plan is foiled!!

答案 1 :(得分:2)

这是一个可以激发你或吓死你的答案:使用monads

特别是ST Monad。不幸的是,我无法向您解释,但我的理解是,在这种情况下它可能很有用。如果你想出来,请告诉我们!

我看到了for my own project

  

您可以复制InvariantLifetime,这是the BTreeMap implementation中实际执行的操作。我说它非常重要的原因是使其属性有用的唯一方法是要求在闭包内创建(或至少,访问)每个文档。

实用的解决方案是不要尝试产生这些编译时错误,而只是在比较中包含父指针:

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