Skip to content

Build trait for optionally abstracting generated builders #401

Description

@nikagamkrelidze

Hello Bon!

In our codebase, we have some trait T and a function f that needs to receive a struct that has T implemented for it. That is a dozen or so structs in our codebase that are implementing T and all of them are built using bon. Therefore, we have to use the f in the following way:

f(struct_1_builder.build());
f(struct_2_builder.build());

This seems unnecessarily repetitive and verbose. Moreover, we expose both, the builders and f to our consumers and would like to simplify API in general. If Bon-based builders could optionally generate implementation of some Build trait (also shipped by bon), we could be simply passing the builders and allow f invoking .build() on them.

Here is an example of what would such a Build trait look like and what would the implementations look like for it:

trait Build {
    type Output;

    fn build(self) -> Self::Output;
}

trait Speaker {
    fn speak(&self);
}

#[derive(bon::Builder)]
struct Dog {
    name: String,
}

impl Speaker for Dog {
    fn speak(&self) {
        println!("Woof, {}", self.name);
    }
}

impl<S> Build for DogBuilder<S>
where
    S: dog_builder::IsComplete,
{
    type Output = Dog;

    fn build(self) -> Self::Output {
        DogBuilder::build(self)
    }
}

#[derive(bon::Builder)]
struct Cat {
    name: String,
}

impl Speaker for Cat {
    fn speak(&self) {
        println!("Meow, {}", self.name);
    }
}

impl<S> Build for CatBuilder<S>
where
    S: cat_builder::IsComplete,
{
    type Output = Cat;

    fn build(self) -> Self::Output {
        CatBuilder::build(self)
    }
}

fn build_and_speak<B>(builder: B)
where
    B: Build,
    B::Output: Speaker,
{
    let item = builder.build();
    item.speak();
}

fn main() {
    build_and_speak(Dog::builder().name("Rex".to_owned()));
    build_and_speak(Cat::builder().name("Whiskers".to_owned()));
}

The generation of the implementation for the builder can be optionally triggered by whatever you see fit, for example some flag on the existing macros.

What do you think of such a feature?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions