2015-05-29 43 views
6

我想比較Obj只有當它們是從相同的Parent對象創建時,它才起作用,但只有一種方法:如果切換比較順序,它不會。如何比較對象,只有當它們是從同一個父對象創建的時候?

下面是最少的代碼:

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

我不認爲壽命分別爲編碼的正確方法。由於第二個父代是在第一個父代之後定義的,因此它的生命週期嚴格在第一個父代的生命週期內。你可以想到的任何生命週期檢查仍然允許任何具有第二生命週期的對象在需要第一生命週期的情況下使用 –

回答

3

我會很樂意解釋一生的方法,但它似乎並不可行。

何時是XY(表示爲X <: Y)的子類型?

構成仿製藥的問題涉及差異

方差回答了這個問題:一個通用型G<X>,這是什麼X <: Y意味着的G<X>G<Y>的關係。

  • 協方差:X <: Y =>G<X> <: G<Y>
  • 不變性:X == Y =>G<X> <: G<Y>
  • 逆變:X <: Y =>G<Y> <: G<X>

Cell<X>是不變WRT X,所以phantom: PhantomData<Cell<&'a Parent>>,使得Child<'a>不變 WRT 'a

PhantomData是一種欺騙你談論差異的方法,只是用你已知的類型來描述它。

這似乎有效,但速度並不快,因爲我們可以創建一個生命週期完全平等的情況,然後測試再次編譯!

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

這是否說明使用生命週期不是解決這個問題的可行方法?如果是這樣,你希望明確說明。 – Shepmaster

+1

我不知道,但這還不夠。這個答案只是一個評論,太長了:) – bluss

+0

@bluss:我記得在這附近玩弄nmatsakis警告過我不要試圖濫用生命* *品牌*:Rust語言不能保證其終身推斷的條款向後兼容;那兩個父母*今天*已經可以用同樣的一生來創造,這意味着我們很幸運,這個計劃現在被挫敗了,而不是下線。 –

2

下面是可以激發你或者嚇唬你到死的答案:使用單子

具體爲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); 
} 
+1

不錯,這是一生中的一些額外信息。請注意,'InvariantLifetime <'a>'正是我提到的'PhantomData >'。 – bluss

相關問題