类型化构建器模式:绕过不变量运行时

问题描述 投票:0回答:1

我想确定是否有一种方法可以遵循类型状态模式,但如果有替代方案,则允许不变量。

假设我有一个围绕特征的包装结构的构建器模式。

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

我不知道如果没有特质专业化等夜间功能,这样的设计模式是否可行。

rust types builder invariants
1个回答
0
投票

解决方案是使用第二个泛型来跟踪要构建的目标类型,然后如果目标类型是

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.