2016-04-07 31 views
6

比方說,我有一些性狀的類型:實現借<Trait>爲實現特質

trait MyTrait { 
    fn function1(&self); 
} 

和某種類型的實現它:

struct MyStruct { 
    number: i32, 
} 
impl MyTrait for MyStruct { 
    fn function1(&self) { 
     printn!("{}", self.number); 
    } 
} 

現在我有另一種類型,它要採取實施MyTrait的事情。它並不關心他們是否擁有。從周圍閱讀,這聽起來像是正確的方式來完成這個是它需要Borrow<X>,而不是X&X或其他什麼。這使得它取型X,或Rc<X>Box<X>,等事情......

我有當X是一個具體類型這個工作,但我怎麼做時X是一個特點它的工作?

這是我第一次嘗試:

struct Consumer<T> { 
    inner: T 
} 

impl<T: Borrow<MyTrait>> Consumer<T> { 
    pub fn new(inner: T) -> Consumer<T> { 
     Consumer { 
      inner: inner 
     } 
    } 
    pub fn do_stuff(&self) { 
     self.inner.borrow().function1(); 
    } 
} 

fn main() { 
    // I want to eventually be able to swap this out for x = Rc::new(MyStruct ... 
    // but I'll keep it simple for now. 
    let x = MyStruct { number: 42 }; 
    let c = Consumer::new(x); 
    c.do_stuff(); 
} 

這還沒有工作,因爲MyStruct實現Borrow<MyStruct>,而不是Borrow<MyTrait>。好吧,讓我們試圖實現:

impl Borrow<MyTrait> for MyStruct { 
    fn borrow(&self) -> &MyTrait { 
     self 
    } 
} 

這給了我下面的錯誤,我不明白:

<anon>:33:5: 35:6 error: method `borrow` has an incompatible type for trait: 
expected bound lifetime parameter , 
    found concrete lifetime [E0053] 
<anon>:33  fn borrow(&self) -> &MyTrait { 
<anon>:34   self 
<anon>:35  } 
<anon>:33:5: 35:6 help: see the detailed explanation for E0053 
error: aborting due to previous error 
playpen: application terminated with error code 101 

什麼?在那裏沒有提到任何具體的生命期,並且定義了沒有任何生命期限的Borrow。我很難過。

首先,我的假設是正確的,使用Borrow是正確的路要走嗎?如果是這樣,我該如何實現Borrow的特質?

回答

6

寫的執行正確的方法是這樣的:

impl<'a> Borrow<MyTrait + 'a> for MyStruct { 
    fn borrow(&self) -> &(MyTrait + 'a) { 
     self 
    } 
} 

特質對象可以綁定一個終生的限制。這是因爲實現特徵的類型可能包含引用,並且在某些情況下,我們需要能夠將依賴於借用對象的對象與不包含對象的對象區分開來。如果沒有指定使用期限,我認爲它默認爲'static;然而,指定&(MyTrait + 'static)作爲返回類型編譯(它不那麼通用,所以你應該喜歡上面的通用解決方案),所以你遇到的問題比這更微妙...

+0

啊,謝謝!我以前從未見過這個&(MyTrait +'a)'語法。我已經在其他類型的泛型參數中看到過,或者在類型邊界中看到過,但以前從來沒有。 –

3

另外,我找到了另一種方法要做到這一點,不需要執行Borrow<MyTrait>可言:

而不必impl<T: Borrow<MyTrait> Consumer<T>的,我們可以Consumer採取指定實際借用類型會是怎樣一個額外的參數,然後限制該類型實現特質。就像這樣:

impl<T: Borrow<Borrowed>, Borrowed: MyTrait> Consumer<T, Borrowed> { 

這需要ConsumerPhantomData成員引用其他未使用Borrowed類型參數。這裏有一個全面實施:

struct Consumer<T, Borrowed> { 
    inner: T, 
    phantom: PhantomData<Borrowed> 
} 

impl<T: Borrow<Borrowed>, Borrowed: MyTrait> Consumer<T, Borrowed> { 
    fn new(inner: T) -> Consumer<T, Borrowed> { 
     Consumer { 
      inner: inner, 
      phantom: PhantomData 
     } 
    } 
    pub fn do_stuff(&self) { // this function is the same as before. 
     self.inner.borrow().function1(); 
    } 
} 

這種替代具有很好的特性,它允許使用特性,裏面還泛型方法,因爲它不需要創建任何特質的對象(特質對象不能特質被提出,具有通用功能:請參閱https://doc.rust-lang.org/error-index.html#method-has-generic-type-parameters)。

其中一個缺點是Consumer現在必須提供關於其通用參數的提示。你必須告訴它具體的類型:

fn main() { 
    let x = MyStruct { number: 42 }; 
    let c = Consumer::<_, MyStruct>::new(x); 
    c.do_stuff(); 
} 
+2

'Borrowed'類型參數是必需的,因爲一個類型可以實現'Borrow ''的多個實例。如果'Borrow'有一個關聯類型,而不是一個類型參數,那麼'impl'上的額外類型參數就沒有必要了;你可以使用'where'子句來添加一個像'T :: Target:MyTrait'這樣的綁定。 'Deref'有一個關聯的類型,但是它沒有爲'T'實現,並且'Target = T'。你或許可以定義你自己的特質,但是你不能利用現有的特質來提供實現,直到實現專業化或負特性邊界。 –

相關問題