[英]How do I construct any of potentially many structs that implement a trait?
我在下面的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>`
我希望能够从FooHolder::new()
方法返回实现TraitFoo
任何潜在结构。 我希望它可以将任何T:TraitFoo
作为返回类型,而不是这种情况下的StructBar
。
我已经尝试了几件事,但是将new()
移至特征中对我没有帮助,因为实现TraitBar
新结构可能会将不同的参数引入new()
。
您似乎步入正轨。 让我们介绍一下要点:
函数fn new() -> FooHolder<T>
实现不能选择类型T
通过调用它的上下文来选择。 因此,我们无法强制执行或始终假定T = StructBar
。
通常情况下,你会做两件事情之一:提供一个构造函数接口, T
通过实施的特点T
。
像将
new()
移入特征中那样对我无济于事,因为实现TraitBar的新结构可能会将不同的参数引入到new()中。
即使您可以做到这一点,编译器又如何知道FooHolder::new()
什么参数呢? 在这里您无法获得可变数量的参数(请参阅如何创建具有可变数量的参数的函数? ),因此,告诉编译器根据T
接受不同数量的参数是不现实的。 但是,我们可以通过具有定义构造参数的关联类型来进行模拟。 在下面的代码中,我采取了使标识符更加惯用的自由( Struct
或Trait
作为前缀只会引入噪音)。
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() }
}
}
否则,如果只想避免创建新的类型参数并公开构造函数方法,则通常将T
移到保持器中没有问题。 实际上,标准库和常用包装箱中的许多API都遵循这种方法。
impl<T> FooHolder<T> {
fn new(foo: T) -> Self {
FooHolder { myfoo: foo }
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.