我想确定是否有一种方法可以遵循类型状态模式,但如果有替代方案,则允许不变量。
假设我有一个围绕特征的包装结构的构建器模式。
trait Trait {
}
struct Wrapper<T: Trait> {
inner: T
}
struct Builder<T: Trait> {
inner: Option<T>
}
impl<T: Trait> Wrapper<T> {
fn builder() -> Builder<T> {
Builder {
inner: None
}
}
}
impl<T: Trait> Builder<T> {
fn with_inner(mut self, inner: T) -> Self {
self.inner = inner;
self
}
fn build(self) -> Option<Wrapper<T>> {
Some(Wrapper {
inner: self.inner?,
})
}
}
Builder::build
返回 Option<Wrapper<T>>
,其中 Option::None
表示构建失败;该构建提供了不变量,即无效参数。
我可以使用类型化构建器模式来防止编译时而不是运行时的不变量。
struct Invariant;
struct TypedBuilder<T> {
inner: T
}
impl<T: Trait> Wrapper<T> {
fn typed_builder() -> TypedBuilder<Invariant> {
TypedBuilder {
inner: Invariant
}
}
}
impl<T> TypedBuilder<T> {
fn with_inner<T2: Trait>(self, inner: T2) -> TypedBuilder<T2> {
TypedBuilder {
inner: inner
}
}
}
impl<T: Trait> TypedBuilder<T> {
fn build(self) -> Wrapper<T> { // infallible
Wrapper {
inner: self.inner,
}
}
}
在此示例中,如果可默认,我希望
TypeBuilder
的 inner
默认为 T
。
所以不变量是:
None
如果T: !Default
我不知道如果没有特质专业化等夜间功能,这样的设计模式是否可行。
解决方案是使用第二个泛型来跟踪要构建的目标类型,然后如果目标类型是
Default
,则允许不变式。
见下文游乐场:
use std::marker::PhantomData;
trait Trait {}
struct Wrapper<T: Trait> {
inner: T
}
struct Invariant;
struct TypedBuilder<Target, T> {
inner: T,
marker: PhantomData<Target>
}
impl<T: Trait> Wrapper<T> {
fn typed_builder() -> TypedBuilder<T, Invariant> {
TypedBuilder {
inner: Invariant,
marker: PhantomData,
}
}
}
impl<Target, T> TypedBuilder<Target, T> {
fn with_inner<T2: Trait>(self, inner: T2) -> TypedBuilder<Target, T2> {
TypedBuilder {
inner: inner,
marker: self.marker,
}
}
}
impl<T: Trait> TypedBuilder<T, T> {
fn build(self) -> Wrapper<T> { // infallible
Wrapper {
inner: self.inner,
}
}
}
impl<T: Trait + Default> TypedBuilder<T, Invariant> {
fn build(self) -> Wrapper<T> { // infallible
Wrapper {
inner: T::default(),
}
}
}
#[derive(Default)]
struct Foo;
struct Bar;
impl Trait for Foo {}
impl Trait for Bar {}
fn main() {
let _foo: Foo = Wrapper::<Foo>::typed_builder()
.build().inner;
let _foo: Foo = Wrapper::<Foo>::typed_builder()
.with_inner(Foo)
.build().inner;
// Can't compile since Bar is not supplied, and is not default
// let _bar: Bar = Wrapper::<Bar>::typed_builder()
// .build().inner;
let _bar: Bar = Wrapper::<Bar>::typed_builder()
.with_inner(Bar)
.build().inner;
}