Expand description
Type-safe partial construction.
The partial attribute macro lets you build a struct field-by-field,
while the type system guarantees that each field is initialized at most once
and that done() is only callable once every field has
been set.
§Example
use partial_this::partial;
#[partial]
pub struct Foo {
pub foo: i32,
pub bar: f32,
}
fn main() {
let foo = Foo::partial(Box::new_uninit())
.with_foo(1)
.with_bar(1.0)
.done();
assert_eq!(foo.foo, 1);
assert_eq!(foo.bar, 1.0);
}§Multiple partial sources
The initial storage can be a Box, Rc, Arc, an owned MaybeUninit, or a mutable
reference, producing Box<T>, Rc<T>, Arc<T>, T, or &mut T respectively:
use partial_this::partial;
use core::mem::MaybeUninit;
#[partial]
pub struct Foo {
pub foo: i32,
pub bar: f32,
}
fn main() {
// From Box<MaybeUninit<T>>
let a: Box<Foo> = Foo::partial(Box::new_uninit())
.with_foo(1)
.with_bar(2.0)
.done();
// From MaybeUninit<T>
let b: Foo = Foo::partial(MaybeUninit::uninit())
.with_foo(1)
.with_bar(2.0)
.done();
// From &mut MaybeUninit<T>
let mut buf = MaybeUninit::uninit();
let c: &mut Foo = Foo::partial(&mut buf)
.with_foo(1)
.with_bar(2.0).done();
}§Field initialization
Each field’s builder method can be called in any order, but at most once:
#[partial]
pub struct Foo {
pub foo: i32,
pub bar: f32,
}
let p = Foo::partial(Box::new_uninit());
let p = p.with_bar(1.0);
let p = p.with_foo(1);
let foo = p.done();
assert_eq!(foo.foo, 1);
assert_eq!(foo.bar, 1.0);A second call is rejected at compile time:
let p = p.with_foo(123);
let p = p.with_foo(456);
// Error: method cannot be called on `...` due to unsatisfied trait bounds§Emplace
Some fields are awkward to build from a ready-made value. Use
emplace_field to initialize one in place — it hands your closure a
&mut MaybeUninit<T> to write into.
This is especially useful when a field
is itself a #[partial] struct: build it directly inside the parent’s
storage, so no intermediate allocation is needed:
#[partial]
pub struct Point {
pub x: i32,
pub y: i32,
}
#[partial]
pub struct Line {
pub start: Point,
pub end: Point,
}
// Place `Point` values directly inside `Line`'s storage.
let line = Line::partial(Box::new_uninit())
.emplace_start(|slot: &mut MaybeUninit<Point>| {
// placement new
Point::partial(slot).with_x(1).with_y(2).done()
})
// or just write
.emplace_end(|slot| slot.write(Point { x: 3, y: 4 }))
.done();
assert_eq!((line.start.x, line.start.y), (1, 2));
assert_eq!((line.end.x, line.end.y), (3, 4));Note: You can write a placement-new constructor to simplify usage, but that is outside the scope of this crate.
impl Point { pub fn new_in<U>(place: U, x: i32, y: i32) -> U::Inited where U: AnyUninit<Target = Self>, { // perhaps some complex calculations Self::partial(place).with_x(x).with_y(y).done() } } let line = Line::partial(Box::new_uninit()) .emplace_start(|slot| Point::new_in(slot, 1, 2)) .emplace_end(|slot| Point::new_in(slot, 3, 4)) .done();
§Field access
After a field is initialized you can read it or mutate it:
#[partial]
pub struct Foo {
pub foo: i32,
pub bar: f32,
}
let mut p = Foo::partial(Box::new_uninit())
.with_foo(1);
assert_eq!(*p.get_foo(), 1);
p.set_foo(123);
let foo = p.with_bar(2.0)
.done();
assert_eq!(foo.foo, 123);§Tuple and generic structs
Tuple structs use 0, 1, … as field method names
#[partial]
pub struct Bar(i32, f32);
let bar = Bar::partial(Box::new_uninit())
.with_0(1)
.with_1(2.0)
.done();
assert_eq!(bar.0, 1);
assert_eq!(bar.1, 2.0);Generic and lifetime-parameterized structs are also supported:
#[partial]
pub struct Pair<'a, T> {
pub name: &'a str,
pub value: T,
}
let pair = Pair::<String>::partial(Box::new_uninit())
.with_name("x")
.with_value(String::from("y"))
.done();
assert_eq!(pair.name, "x");
assert_eq!(pair.value, "y");§Others
- Multiple partial sources:
Box::new_uninit(),MaybeUninit::uninit()or&mut MaybeUninitall work as the initial storage. - Field initialization methods: each field offers
with_field(value),emplace_field(init), anduninit_field(); the builder methods can be called in any order, and at most once (a second call is a compile time error). - Field access methods: after a field is initialized you can read it with
get_field(), mutate it withget_mut_field(), or assign withset_field(value). done(): finalizes the builder once every field is initialized, both as an inherent method and through theCtorCompletetrait.- Drop safety: partially-built values drop already-initialized fields in reverse order of initialization (the last field set is dropped first).
- Field visibility: the builder type
PartialStructNameis re-exported for a structStructName, and field methods are available directly on the builder.
§Safety
The unsafe code in this crate is self-contained: it writes initialized fields
into a MaybeUninit buffer and only assumes the target value is initialized
after every field has been written.
Re-exports§
pub use typenum;
Traits§
- AnyUninit
- A source of uninitialized storage for a value of type
Target. - Ctor
Complete - Finalizes a partially-constructed value once every field is initialized.
Attribute Macros§
- partial
- Generates a type-safe partial construction for a struct.