#builder-pattern #builder #macro #methods #macro-derive #cascading

no-std using

A macro for simpler builders with method cascading

2 unstable releases

0.1.0 May 1, 2024
0.0.0 Apr 10, 2024

#2451 in Rust patterns

Download history 5/week @ 2024-09-22 3/week @ 2024-09-29

82 downloads per month

MIT license

34KB
571 lines

using

Crates.io Version docs.rs Crates.io License

The using macro allows simplified implementation and usage of the builder pattern without having to return &mut Self or Self from the builder methods:

let vec3 = using!(Vec3Builder::default() => {
    .x(4.27);
    .y(9.71);
    .z(13.37);
    .build()
});

#[derive(Default, Debug, Copy, Clone)]
struct Vec3Builder {
    x: Option<f32>,
    y: Option<f32>,
    z: Option<f32>,
}

impl Vec3Builder {
    pub fn x(&mut self, x: f32) {
        self.x = Some(x);
    }

    pub fn y(&mut self, y: f32) {
        self.y = Some(y);
    }

    pub fn z(&mut self, z: f32) {
        self.z = Some(z);
    }

    //this also works with `self` instead of `&mut self`
    pub fn build(&mut self) -> Vec3 {
        Vec3 {
            x: self.x.unwrap(),
            y: self.y.unwrap(),
            z: self.z.unwrap(),
        }
    }
}

#[derive(Debug, Copy, Clone)]
struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}

The using macro allows calling multiple methods on the same object, which is also known as method cascading. In the example above, the following code is generated:

let vec3 = {
    let mut target = Vec3Builder::default();
    target.x(4.27);
    target.y(9.71);
    target.z(13.37);
    target.build()
};

This allows more flexibility for implementing and using builders and also makes more complicated use-cases more ergonomic:

// "Conventional" builder with method chaining:

let mut builder = SomeBuilder::new()
    .x(...)
    .y(...);
if some_condition {
    builder.z(...);
}
if some_other_condition {
    some_function(&mut builder);
}
let thing = builder.build();



// Using Builder with `using`:

let thing = using!(builder @ SomeBuilder::new() => {
    .x(...);
    .y(...);
    if some_condition {
        .z(...);
    }
    if some_other_condition {
        some_function(&mut builder);
    }
    .build()
});

Although using was primarily designed for the builder pattern, it is not limited to that as it can be used on basically every type:

let hello_world = using!(Vec::new() => {
    .push("Hello");
    .push("World!");
    .join(", ")
});
assert_eq!(hello_world, "Hello, World!");

No runtime deps