Skip to content

Change Rust builders #367

@tjpalmer

Description

@tjpalmer

We currently make builders like this:

#[derive(Clone, Default)]
pub struct HiOptions {
    pub i: Option<i32>
}

#[derive(Clone)]
pub struct HiBuilder<T: ..., U: ...> {
    pub t: Option<T>, pub u: U
}

impl<T: ..., U: ...> HiBuilder<T, U> {
    pub fn build(self) -> Hi<T, U> {
        self.build_with(std::default::Default::default())
    }
    pub fn build_with(self, options: HiOptions) -> Hi<T, U> {
        Hi::new(self.t, self.u, options.i)
    }
}

But we can easily make naming conflicts with the name ...Builder, even though that's idiomatic. On the other hand, the builder style above isn't the most common. I was a bit creative making structs with pub fields. Something like this would be more common:

#[derive(Clone, Default)]
pub struct HiBuilder<T: ..., U: ...> {
    t: Option<Option<T>>, u: Option<U>, i: Option<i32>
}

impl<T: ..., U: ...> HiBuilder<T, U> {
    pub fn t(mut self, t: Option<T>) -> Self {
        self.t = Some(t);
        self
    }
    pub fn u(mut self, u: U) -> Self {
        self.u = Some(u);
        self
    }
    pub fn i(mut self, i: i32) -> Self {
        self.i = Some(i);
        self
    }
    pub fn build(self) -> Hi<T, U> {
        Hi::new(self.t.unwrap(), self.u.unwrap(), self.i)
    }
}

If we coalesce Builder and Options like that (where the abstraction here also helps #271), we can adjust that further to something like this:

pub impl<T: ..., U: ...> Hi<T, U> {
    pub fn builder() -> HiBuilder<T, U> {
        builders::HiBuilder::default()
    }

    ...
}

pub mod builders {
    #[derive(Clone, Default)]
    pub struct HiBuilder<T: ..., U: ...> {
        t: Option<Option<T>>, u: Option<U>, i: Option<i32>
    }

    impl<T: ..., U: ...> HiBuilder<T, U> {
        pub fn t(mut self, t: Option<T>) -> Self {
            self.t = Some(t);
            self
        }
        pub fn u(mut self, u: U) -> Self {
            self.u = Some(u);
            self
        }
        pub fn i(mut self, i: i32) -> Self {
            self.i = Some(i);
            self
        }
        pub fn build(self) -> Hi<T, U> {
            super::Hi::new(self.t.unwrap(), self.u.unwrap(), self.i)
        }
    }
}

Then you access through the static method, such as Hi::<A, B>::builder().t(...).u(...).build() or some such. This might be more convenient and idiomatic while reducing risk of collision with Temper naming conventions.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions