Skip to main content

Crate partial_this

Crate partial_this 

Source
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 MaybeUninit all work as the initial storage.
  • Field initialization methods: each field offers with_field(value), emplace_field(init), and uninit_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 with get_mut_field(), or assign with set_field(value).
  • done(): finalizes the builder once every field is initialized, both as an inherent method and through the CtorComplete trait.
  • 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 PartialStructName is re-exported for a struct StructName, 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.
CtorComplete
Finalizes a partially-constructed value once every field is initialized.

Attribute Macros§

partial
Generates a type-safe partial construction for a struct.