Я хотел бы определить, есть ли способ следовать шаблону состояния типа, но разрешить инварианты, если есть альтернатива.
Предположим, у меня есть шаблон построения структуры-обертки вокруг признака.
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
Я не знаю, возможен ли такой шаблон проектирования без ночных функций, таких как специализация черт.
@harmic Почти, мне нужна ошибка компиляции, если T
не указан и он не реализует Default
. Если T
указан, то Default
не требуется.
Итак, если кто-то создает TypedBuilder
, а затем вызывает build
без предварительного вызова with_inner
&& T не реализует Default
, то вам нужна ошибка компиляции? Если да - однозначно невозможно.
@harmic, это определенно возможно, см. мой ответ ниже.
Умно, я бы никогда об этом не подумал.
Решение заключалось в том, чтобы использовать второй обобщенный тип для отслеживания создаваемого целевого типа, а затем разрешить инвариант, если целевой тип равен 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;
}
Итак, вы имеете в виду: если вызывающая сторона не предоставляет значение для
inner
, вызываяwith_inner
в сборщике, то он должен создать значение по умолчанию? И поэтому, еслиT
не реализуетсяDefault
, вам нужна ошибка компиляции?