nicer_builder
This repository endeavors to develop a builder derive macro that is more intelligent and cognizant of the existing builder context.
Observation
Consider having a Rust struct as follows:
Now, suppose you wish to implement a builder pattern for this struct; it could be crafted as follows:
which can be later use like this:
let user = builder
.name
.age
.address
.build
.unwrap;
println!;
Regardless of whether you favor this approach, it presents several challenges:
The build method returns a Result
Although the builder technically knows its state, we are compelled to return the User instance, constructed by the builder, wrapped in a Result
Builder lacks awareness of fields already set
This results in the following sequence of methods being entirely feasible:
let user = builder
.name
.age
.age
.address
.age
.build
.unwrap;
Suboptimal IDE completion support
This issue stems from the previous one; when I enter:
let user = builder
.name
.
Upon receiving this . input, the language server protocol (LSP) proposes the complete list of builder methods - name, age, and address, even though name has just been set. Furthermore, the visibility of the build method is unrestricted - you can invoke it in the midst of the build process, potentially leading to a panic when attempting tounwrap the result.
Manual implementation
Hence, introducing a new field to the User struct will necessitate corresponding adjustments on the Builder side.
Solution?
This repository offers a solution to these issues by providing a proc_macro that automatically generates the Builder implementation for the struct, devoid of the aforementioned problems.
let alice = builder
.with_address
.with_age
.with_name
.build;
Notably, the build method no longer returns a Result; instead, it directly returns the actual User instance, and it is guaranteed not to fail at compile time.
Downsides
The proc_macro provided by this crate essentially generates a comprehensive state machine. Each node of this machine contains a dedicated sub-builder implementation, defining its own set of methods. In simpler terms, the proc_macro would generate approximately 2^(number of fields) new structs, which can become costly quicker than one might prefer.