如何构造任何可能实现特征的结构?

时间:2017-05-27 19:23:58

标签: generics rust traits

我已经在Rust下面创建了一个小问题的小例子:

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>`

我希望能够从TraitFoo方法返回任何可能实现FooHolder::new()的结构体。在这种情况下,我希望任何T:TraitFoo作为返回类型而不仅仅是StructBar

我尝试过几件事情,但是将new()移到特质中并不能帮助我,因为实现TraitBar的新结构可能会将不同的参数带入new()

1 个答案:

答案 0 :(得分: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 }
    }
}
相关问题