我如何构造实现特征的潜在许多结构中的任何一个?
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>`
我希望能够 return 从 FooHolder::new()
方法实现 TraitFoo
的潜在许多结构中的任何一个。在这种情况下,我希望它期望任何 T:TraitFoo
作为 return 类型而不仅仅是 StructBar
。
我已经尝试了几种方法,但是像将 new()
移动到特征中这样的东西对我没有帮助,因为实现 TraitBar
的新结构可能会在 new()
中采用不同的参数。
您似乎几乎走在了正确的轨道上。让我们介绍一下要点:
函数 fn new() -> FooHolder<T>
的实现无法选择类型 T
。这是由调用它的上下文选择的。所以我们不能强制执行,或者总是假设 T = StructBar
.
通常,您会做以下两件事之一:在 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().
即使您可以这样做,编译器如何知道 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
移入 holder 通常没有问题。事实上,标准库和流行的 crate 中的许多 API 都遵循这种方法。
impl<T> FooHolder<T> {
fn new(foo: T) -> Self {
FooHolder { myfoo: foo }
}
}
我创建了一个小的工作示例来说明我在下面的 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>`
我希望能够 return 从 FooHolder::new()
方法实现 TraitFoo
的潜在许多结构中的任何一个。在这种情况下,我希望它期望任何 T:TraitFoo
作为 return 类型而不仅仅是 StructBar
。
我已经尝试了几种方法,但是像将 new()
移动到特征中这样的东西对我没有帮助,因为实现 TraitBar
的新结构可能会在 new()
中采用不同的参数。
您似乎几乎走在了正确的轨道上。让我们介绍一下要点:
函数 fn new() -> FooHolder<T>
的实现无法选择类型 T
。这是由调用它的上下文选择的。所以我们不能强制执行,或者总是假设 T = StructBar
.
通常,您会做以下两件事之一:在 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().
即使您可以这样做,编译器如何知道 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
移入 holder 通常没有问题。事实上,标准库和流行的 crate 中的许多 API 都遵循这种方法。
impl<T> FooHolder<T> {
fn new(foo: T) -> Self {
FooHolder { myfoo: foo }
}
}