[英]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). 在下面的代码中,我采取了使标识符更加惯用的自由(
Struct
或Trait
作为前缀只会引入噪音)。
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() }
}
}
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.