简体   繁体   English

如何构造可能实现特征的许多潜在结构中的任何一个?

[英]How do I construct any of potentially many structs that implement a trait?

I have created a small working example of a problem I'm having in Rust below: 我在下面的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();
}

This fails to compile with : 无法使用以下命令进行编译:

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

I would like to be able to return any of potentially many structs that implement TraitFoo from the FooHolder::new() method. 我希望能够从FooHolder::new()方法返回实现TraitFoo任何潜在结构。 I'd like it to expect any T:TraitFoo as a return type instead of just StructBar in this case. 我希望它可以将任何T:TraitFoo作为返回类型,而不是这种情况下的StructBar

I have tried several things but something like moving new() into the trait wouldn't help me as new structs that implement TraitBar might take different arguments into new() . 我已经尝试了几件事,但是将new()移至特征中对我没有帮助,因为实现TraitBar新结构可能会将不同的参数引入new()

You seem to be almost on the right track. 您似乎步入正轨。 Let's cover the essentials: 让我们介绍一下要点:

An implementation of a function fn new() -> FooHolder<T> cannot choose the type T . 函数fn new() -> FooHolder<T>实现不能选择类型T That is chosen by the context where it's called. 通过调用它的上下文来选择。 So we cannot enforce, or always assume that T = StructBar . 因此,我们无法强制执行或始终假定T = StructBar

Usually, you would do one of two things: provide a constructor interface to T in a trait implemented by T . 通常情况下,你会做两件事情之一:提供一个构造函数接口, T通过实施的特点T

something like moving new() into the trait wouldn't help me as new structs that implement TraitBar might take different arguments into new(). 像将new()移入特征中那样对我无济于事,因为实现TraitBar的新结构可能会将不同的参数引入到new()中。

Even if you could do that, how would the compiler know what arguments to expect in FooHolder::new() ? 即使您可以做到这一点,编译器又如何知道FooHolder::new()什么参数呢? Variable number of arguments are out of your reach here (see How can I create a function with a variable number of arguments? ), so telling the compiler to accept a different number of arguments depending on T is not realistic. 在这里您无法获得可变数量的参数(请参阅如何创建具有可变数量的参数的函数? ),因此,告诉编译器根据T接受不同数量的参数是不现实的。 However, we can emulate that by having an associated type that defines construction parameters. 但是,我们可以通过具有定义构造参数的关联类型来进行模拟。 In the following code, I took the liberty of making the identifiers more idiomatic ( Struct or Trait as prefixes only introduce noise). 在下面的代码中,我采取了使标识符更加惯用的自由( StructTrait作为前缀只会引入噪音)。

trait Foo {
    type Params; // new type parameter

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

    fn foo(&self) -> i32;
}

Our Bar would be defined like this: 我们的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 }
    }
}

And our FooHolder would now be able to construct a value of type T : 并且我们的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) }
    }
}

Using FooHolder : 使用FooHolder

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

When no arguments are needed to construct T , we can rely on the Default trait: 当不需要参数来构造T ,我们可以依靠Default特性:

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

Full Playground 完整的游乐场

Otherwise, in case you just want to avoid making new type parameters and exposing constructor methods, there is usually no problem with moving T into the holder. 否则,如果只想避免创建新的类型参数并公开构造函数方法,则通常将T移到保持器中没有问题。 In fact, many APIs in the standard library and popular crates follow this approach. 实际上,标准库和常用包装箱中的许多API都遵循这种方法。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM