Crate typesafe_builders
source ·Expand description
Type Safe Builder Pattern
Infallible compile-time checked builders for your structs.
No more worrying whether the build call on your builder will return Ok or not. Maybe you forgot to set a field? typesafe-builders solves this by using the Rust type-system to ensure correct usage.
Example
use typesafe_builders::prelude::*;
fn main() {
#[derive(Builder)]
struct Point {
#[builder(constructor)]
x: u8,
y: u8,
#[builder(optional)]
z: Option<u8>,
}
// `builder` requires `x` since it is marked as `constructor`.
let builder = Point::builder(1);
// These do not compile:
// partial.x(6); // `x` is already set
// partial.build(); // `y` is not set
// `build` is only available once all required fields are set:
let result = builder.y(2).build();
assert_eq!(result.x, 1);
assert_eq!(result.y, 2);
assert_eq!(result.z, None);
}Field Attributes
All attributes must be wrapped by builder, eg. builder(optional).
optional- A field can be set, but is not required to.constructor- A field must already be set in thebuilderfunction.
Known Downside
Although having everything known at compile time it nice - it comes at the cost of having verbose types in cases where that information needs to be passed on.
For example when you want to return a builder from a function, it normally looks like this:
use typesafe_builders::prelude::*;
#[derive(Builder)]
struct Point {
x: u8,
y: u8,
z: u8,
}
// Ugly type name here...
fn preset() -> GenericPointBuilder<false, false, true> {
Point::builder().z(0)
}
fn main() {
// Luckily we dont need to type it here again:
let partial = preset();
let point = partial.x(1).y(2).build();
}Please open an MR/Issue if you know how to improve this.
How does it work?
Const generic one-hot bitfields. What you get is similar to this:
pub struct Builder<const x_set: bool, const y_set: bool> {
x: Option<u8>,
y: Option<u8>,
}
impl<const y_set: bool> Builder<false, y_set> {
fn set_x(self, x: u8) -> Builder<true, y_set,> {
unimplemented!()
}
}
impl<const x_set: bool> Builder<x_set, false> {
fn set_y(self, y: u8) -> Builder<x_set, true> {
unimplemented!()
}
}
// The build function is only available once all fields are set:
impl Builder<true, true> {
fn build() {
}
}TODOs
-
Add
optionalfields. -
Add
renamefield attribute. -
Add
constructoror something like this to have mandatory args directly in thebuilderfunction. -
Add
Intoor whatever to cast types. -
Add way to pass options as
Someautomatically. - Cleanup
Modules
- Selection of imports that “just work”.