2017-03-27 14 views
1

這裏是我想要達到一個人爲的例子:如何在Rust中編寫一個接受任何滿足特性的迭代器的函數?

trait Double { 
    fn get(&self) -> i32; 
} 

impl Double for i32 { 
    fn get(&self) -> i32 { self * 2 } 
} 

fn foo<'a, I: Iterator<Item = &'a Double>>(is: I) { 
    for i in is { 
     println!("{}", i.get()); 
    } 
} 

fn main() { 
    let is = vec![1, 2, 3, 4]; 
    foo(is.into_iter()); 
} 

錯誤這裏說的「預期完整的變量,發現&Double」。

我遇到了麻煩,因爲無處不在談論作爲特徵的迭代器。我試圖做甚至可能嗎?

回答

3

綁定的Iterator<Item = &'a Double>>說,你想這將產生完全型&Double的項目的迭代器,其表示特徵Double的特徵對象。但是你需要一個迭代器來產生任何類型的實現Double特性的類型。這聽起來非常相似,因此令人困惑,但這完全是關於動態與靜態調度。您應該閱讀關於trait objects的Rust書籍章節,以瞭解究竟發生了什麼。

但快速摘要:有寫作

fn foo<T: MyTrait>(t: &T) {} 

fn foo(t: &MyTrait) {} 

你寫的代碼等同於後者之間的差異,但實際上希望是前者。


那麼你如何表達你的代碼意圖?一種可能性是引入另一個類型參數!

fn foo<'a, T, I>(is: I) 
    where T: Double, 
      I: Iterator<Item = &'a T>, 
{ 
    for i in is { 
     println!("{}", i.get()); 
    } 
} 

但你也可以只約束迭代器的相關類型(見Francis Gagné's answer):

fn foo<I>(is: I) 
    where I: Iterator, 
      I::Item: Double, 
{ ... } 

然而,這兩個版本略有不同,因爲一個接受以實施Double類型的Iterator過引用而另一個則直接遍歷實現Double的類型。只要使用最適合你的東西,或者將這兩種東西概括爲AsRef等特性。

+0

啊不錯,所以這是靜態調度呀?我不想通過這樣做引入開銷。 –

+1

@SamKellett是的,這是靜態調度(動態調度只有在看到'&MyTrait','&mut MyTrait'或'Box '時纔會使用) –

3

是的,這是可能的。您需要使用where子句來指定關聯類型I::Item上的限制。

fn foo<I>(is: I) 
    where I: Iterator, 
      I::Item: Double, 
{ 
    for i in is { 
     println!("{}", i.get()); 
    } 
} 

(我也感動綁定到where子句I: Iterator所有邊界保持在一起。)

+0

這很好,因爲它不會添加額外的類型參數 –

+0

是的,這比我原來的解決方案更好。我也將這個解決方案添加到我的答案中,因爲您(Sam)已經接受了我的答案。我希望你不介意,弗朗西斯。 –

+1

沒問題。不過,我們的解決方案並不完全相同:我的版本請求double的迭代器,而您的版本請求* references *的迭代器加倍;你可能想提到這一點。 –

相關問題