typesafe-builders 0.4.1

Infallible compile-time checked builders for your structs.
Documentation

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 the builder function.

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 optional fields.
  • Add rename field attribute.
  • Add constructor or something like this to have mandatory args directly in the builder function.
  • Add Into or whatever to cast types.
  • Add way to pass options as Some automatically.
  • Cleanup