我想定义一个特征,该特征具有另一个特征,该特征具有自己的特征对象类型作为关联类型:
/// A trait for making things.
trait Make {
type Output: ?Sized;
fn make(self: Box<Self>) -> Box<Self::Output>;
}
/// A special case of Make, which makes things that impl the same trait.
trait Bootstrapper: Make<Output = dyn Bootstrapper> {} // Will NOT compile.
但是,我无法执行此操作,因为它会创建无限循环。在上面的示例中,我需要为Output
(其本身为dyn Bootstrapper
)指定dyn Bootstrapper
。但是然后,我需要为that Output
指定dyn Bootstrapper
,依此类推,例如Make<Output = dyn Bootstrapper<Output = dyn Bootstrapper<Output = dyn Bootstrapper<...>>>
。
Rust编译器似乎同意这将不起作用:
error[E0391]: cycle detected when computing the supertraits of `Bootstrapper`
--> src/lib.rs:8:1
|
8 | trait Bootstrapper: Make<Output = dyn Bootstrapper> {} // Will NOT compile.
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: ...which again requires computing the supertraits of `Bootstrapper`, completing the cycle
note: cycle used when collecting item types in top-level module
--> src/lib.rs:8:1
|
8 | trait Bootstrapper: Make<Output = dyn Bootstrapper> {} // Will NOT compile.
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我也不能指定Output = Self
,因为这会过度限制特征,因此,Bootstrapper
的给定实现只能将make()
本身更多。我希望Bootstrapper
能够make()
其他种类的Bootstrapper
。请参阅此Rust playground作为(大致)我正在尝试做的示例。
是否有解决此问题的方法,并让Bootstrapper
指定Bootstrapper
(not Self
)为Output
?
我最终得到的解决方案是这样:
/// A meta-`trait` that defines the associated types for a subset of `Make`.
trait Traits {
type Output : ?Sized;
}
/// A `trait` for making things. Takes in a set of `Traits` that define the
/// output for the `make()` function.
trait Make {
type Traits : Traits;
fn make(self : Box<Self>) -> Box<<Self::Traits as Traits>::Output>;
}
通过将关联的type
分离为单独的Traits
元接口,我能够为新的具体类型实现Traits
并将其传递给Make
:
/// `Traits` implementation for `Bootstrapper`, which constrains the `Make`
/// implementation for `Bootstrapper` to outputting a `Box<Bootstrapper>`.
struct BootstrapperTraits;
impl Traits for BootstrapperTraits {
// NOTE: Specifying Self here gets around the circular dependency.
type Output = dyn Bootstrapper<Traits = Self>;
}
/// A special case of Make that makes the same *kind* of thing as itself, but
/// not *necessarily* `Self`.
trait Bootstrapper : Make<Traits = BootstrapperTraits> {
fn is_best(&self) -> bool;
}
具体类型(BootstrapperTraits
)可以通过将Self
指定为type Traits
的Output
来避开圆形特征。然后,将BootstrapperTraits
指定为Traits
的特定Bootstrapper
实现。因此,实现Bootstrapper
的任何人现在还必须实现Make<Traits = BootstrapperTraits>
,这要求Output
是dyn Bootstrapper
。
请参阅此Rust playground了解完整的解决方案。