2017-05-27 58 views
0

我創建我有魯斯特以下問題的一小工作示例:我如何構建任何實現特質的潛在許多結構?

trait TraitFoo { 
    fn foo(&self) -> i32; 
} 

struct StructBar { 
    var: i32, 
} 

impl TraitFoo for StructBar { 
    fn foo(&self) -> i32 { 
     self.var 
    } 
} 

impl StructBar { 
    fn new() -> StructBar { 
     StructBar { var: 5 } 
    } 
} 

struct FooHolder<T: TraitFoo> { 
    myfoo: T, 
} 

impl<T: TraitFoo> FooHolder<T> { 
    fn new() -> FooHolder<T> { 
     FooHolder { myfoo: StructBar::new() } 
    } 
} 

fn main() { 
    let aaa = FooHolder::new(); 
} 

這失敗,編譯:

error[E0308]: mismatched types 
    --> src/main.rs:27:9 
    | 
27 |   FooHolder { myfoo: StructBar::new() } 
    |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `StructBar` 
    | 
    = note: expected type `FooHolder<T>` 
       found type `FooHolder<StructBar>` 

我希望能夠回到任何的可能有許多結構體從FooHolder::new()方法實施TraitFoo。我希望它在這種情況下可以預期任何T:TraitFoo作爲返回類型,而不僅僅是StructBar

我已經嘗試了幾件事情,但像將new()移入特徵之類的東西不會幫助我,因爲實施TraitBar的新結構可能會將不同的參數帶入new()

回答

1

你似乎幾乎走在正確的軌道上。讓我們探討一下要領:

函數fn new() -> FooHolder<T>的實現不能選擇T類型。這是在被調用的上下文中選擇的。所以我們不能執行,或者總是假設T = StructBar

通常,您可以執行以下兩件事之一:在由T實現的特徵中提供T的構造函數接口。

類似將new()移入特徵不會幫助我,因爲實現TraitBar的新結構可能會將不同的參數帶入new()。

即使你能做到這一點,如何將編譯器知道FooHolder::new()期待什麼參數?可變數目的參數在這裏不可用(請參閱How can I create a function with a variable number of arguments?),因此告訴編譯器根據T接受不同數量的參數是不現實的。但是,我們可以通過具有定義構造參數的關聯類型來模擬。在下面的代碼中,我冒昧地使標識符更具慣用性(StructTrait作爲前綴僅引入噪聲)。

trait Foo { 
    type Params; // new type parameter 

    fn new(params: Self::Params) -> Self; // new static method 

    fn foo(&self) -> i32; 
} 

我們Bar會這樣定義:

struct Bar { 
    var: i32, 
} 

impl Foo for Bar { 
    type Params = i32; 

    fn foo(&self) -> i32 { 
     self.var 
    } 

    fn new(params: Self::Params) -> Self { 
     Bar { var: params } 
    } 
} 

而且我們FooHolder現在能夠構建T類型的值:

struct FooHolder<T: Foo> { 
    myfoo: T, 
} 

impl<T: Foo> FooHolder<T> { 
    fn new(params: T::Params) -> FooHolder<T> { 
     FooHolder { myfoo : T::new(params) } 
    } 
} 

使用FooHolder

let aaa = FooHolder::<Bar>::new(5); 

時,不需要參數來構建T,我們可以依靠Default特點:

impl<T: Foo + Default> Default for FooHolder<T> { 
    fn default() -> Self { 
     FooHolder { myfoo: T::default() } 
    } 
} 

Full Playground

否則,如果你只是想避免製造新的類型參數和揭露構造方法,將T移入持有者通常沒有問題。事實上,標準庫和流行箱子中的許多API都遵循這種方法。

impl<T> FooHolder<T> { 
    fn new(foo: T) -> Self { 
     FooHolder { myfoo: foo } 
    } 
} 
+0

謝謝!這是非常徹底的,正是我所期待的。 – Kevin

相關問題