1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/// Partially initialize a struct wrapped in `MaybeUninit`.
///
/// The specified fields will be updated with the given values, and mutable references to those
/// fields will be returned.
/// This statically ensures that the same field is not set mutltiple times in the same macro call,
/// and that multiple references to the same value are not returned.
///
/// This must be used in an `unsafe` block or function when accessing fields of unions.
///
/// ## Syntax
/// ```
/// # use core::mem::MaybeUninit;
/// # use project_uninit::partial_init;
/// # #[derive(PartialEq, Eq, Debug)]
/// # struct Person { name: Name, age: u32, id: (usize, usize) }
/// # #[derive(PartialEq, Eq, Debug)]
/// # struct Name { first: &'static str, last: &'static str }
/// # let mut bob = MaybeUninit::new(Person {
/// #     name: Name { first: "Bob1", last: "Jones1" },
/// #     age: 34, id: (111, 222),
/// # });
/// // Initialize a single field:
/// let age: &mut u32 = partial_init!(bob => age = 35);
/// assert_eq!(*age, 35);
///
/// // Initialize multiple fields:
/// let (age, id) = partial_init!(bob => {
///     age: 36,
///     id: (111, 222),
/// });
/// assert_eq!(*age, 36);
/// assert_eq!(*id, (111, 222));
///
/// // Initialize fields of fields:
/// let first: &mut &'static str = partial_init!(bob => name => first = "Bob");
/// assert_eq!(*first, "Bob");
///
/// // Initialize fields of tuples (also works for tuple structs):
/// let id0: &mut usize = partial_init!(bob => id => 0 = 444);
/// assert_eq!(*id0, 444);
///
/// // Initialize multiple fields, including nested fields:
/// let (last, age, id1) = partial_init!(bob => {
///     name => last: "Jones",
///     age: 37,
///     id => 1: 888,
/// });
///
/// assert_eq!(*last, "Jones");
/// assert_eq!(*age, 37);
/// assert_eq!(*id1, 888);
/// ```
///
///
/// ## Example
/// ```
/// use core::mem::MaybeUninit;
/// use project_uninit::partial_init;
///
/// #[derive(PartialEq, Eq, Debug)]
/// struct Person { name: Name, age: u32, id: (usize, usize) }
/// #[derive(PartialEq, Eq, Debug)]
/// struct Name { first: &'static str, last: &'static str }
///
/// let mut alice = MaybeUninit::<Person>::uninit();
///
/// let age = partial_init!(alice => age = 20);
/// assert_eq!(*age, 20);
/// *age = 22;
///
/// let (first, last, id) = partial_init!(alice => {
///     name => first: "Alice",
///     name => last: "Smith",
///     id: (123, 456),
/// });
/// assert_eq!(*first, "Alice");
/// assert_eq!(*last, "Smith");
/// assert_eq!(*id, (123, 456));
///
/// partial_init!(alice => id => 1 = 789);
///
/// assert_eq!(unsafe { alice.assume_init() }, Person {
///     name: Name { first: "Alice", last: "Smith" },
///     age: 22,
///     id: (123, 789),
/// });
/// ```
#[macro_export]
macro_rules! partial_init {
    // intialize multiple fields
    ($expr:expr => {$($($props:tt)=>+ : $val:expr),* $(,)?}) => {{
        // generate an error message if a field is used more than once
        $crate::__assert_unique!($expr, [ $( [ $($props).+ ] )* ]);
        #[allow(unused_imports)]
        use ::core::borrow::BorrowMut;
        let _ref: &mut ::core::mem::MaybeUninit<_> = $expr.borrow_mut();
        let ptr = _ref.as_mut_ptr();
        let lt = $crate::utils::bind_mut_lt(_ref);

        if false {
            // this will never be executed
            // it's only to assert that it is safe to access the fields
            #[allow(unused_unsafe)]
            let _x = unsafe { &mut *ptr };
            let _y = ($(&mut _x.$($props).+,)*);
        }
        ($({
            let prop_ref;
            #[allow(unused_unsafe)]
            unsafe {
                let prop_ptr = ::core::ptr::addr_of_mut!((*ptr).$($props).+);
                ::core::ptr::write(prop_ptr, $val);
                prop_ref = $crate::utils::deref_ptr_with_lt(prop_ptr, lt);
            }
            prop_ref
        },)*)
    }};

    // initialize a single field
    ($expr:expr => $($props:tt)=>+ = $val:expr) => {
        $crate::partial_init!($expr => { $($props)=>+: $val }).0
    };
}

///```compile_fail
/// use project_uninit::partial_init;
/// use core::mem::MaybeUninit;
/// struct Foo { a: i32, b: u32 }
/// let mut x = MaybeUninit::<Foo>::uninit();
/// let _ = partial_init!(x => { a: 1, b: 6, a: -1 });
///```
fn _test_multiple_per_macro_call_fails() {}

///```compile_fail
/// use project_uninit::{partial_init, project_uninit};
/// use core::mem::MaybeUninit;
/// struct Foo { a: i32, b: u32 }
/// let mut x = MaybeUninit::<Foo>::uninit();
/// let a = project_uninit!(x => a);
/// partial_init!(x => { b: 6 });
/// let a1 = a;
///```
fn _partial_init_with_existing_field_borrow_fails() {}

///```compile_fail
/// use project_uninit::{partial_init, project_uninit_mut};
/// use core::mem::MaybeUninit;
/// struct Foo { a: i32, b: u32 }
/// let mut x = MaybeUninit::<Foo>::uninit();
/// let a = project_uninit_mut!(x => a);
/// *a = MaybeUninit::new(1);
/// partial_init!(x => { b: 6 });
/// *a = MaybeUninit::new(3);
///```
fn _partial_init_with_existing_mut_field_borrow_fails() {}