const_destructure/
lib.rs

1#[doc(hidden)]
2/// Provides access to the inner value of a ManuallyDrop<T>.
3pub const fn __manually_drop_inner_ref<T>(slot: &core::mem::ManuallyDrop<T>) -> &T {
4    // SAFETY: ManuallyDrop<T> and T are guaranteed to have the same layout
5    unsafe { core::mem::transmute(slot) }
6}
7
8#[macro_export]
9macro_rules! const_destructure {
10    // struct
11    (let $S:path { $($field_spec:tt)* } = $value:expr) => {
12        $crate::const_destructure!(@struct ($($field_spec)*) => let $S {} = $value);
13    };
14    // tuple
15    (let ($($var:pat_param),* $(,)?) = $value:expr) => {
16        $crate::const_destructure!(@tuple ($($var),*); (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) => (); let () = $value);
17    };
18    (@struct ()
19        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
20    ) => {
21        let value = $value;
22        let value = ::core::mem::ManuallyDrop::new(value);
23        let value = $crate::__manually_drop_inner_ref(&value);
24        let __destructures_all_fields_and_fields_are_unique = || {
25            // SAFETY: NOT SAFE BUT WE DON'T CALL THIS FUNCTION
26            #[allow(unused)]
27            let $S { $($field: $var),* } = unsafe { ::core::ptr::read(value) };
28        };
29        // SAFETY: We avoid double free by 1) only reading each field once (since they're unique) and 2) the original is wrapped in ManuallyDrop.
30        $(
31            let $var = unsafe { ::core::ptr::addr_of!(value.$field).read() };
32        )*
33    };
34    (@struct ($next_field:ident: $next_var:pat_param $(,)?)
35        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
36    ) => {
37        $crate::const_destructure!(@struct () => let $S { $($field: $var,)* $next_field: $next_var } = $value);
38    };
39    (@struct ($next_field:ident $(,)?)
40        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
41    ) => {
42        $crate::const_destructure!(@struct () => let $S { $($field: $var,)* $next_field: $next_field } = $value);
43    };
44    (@struct ($next_field:ident: $next_var:pat_param, $($rest:tt)*)
45        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
46    ) => {
47        $crate::const_destructure!(@struct ($($rest)*) => let $S { $($field: $var,)* $next_field: $next_var } = $value);
48    };
49    (@struct ($next_field:ident, $($rest:tt)*)
50        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
51    ) => {
52        $crate::const_destructure!(@struct ($($rest)*) => let $S { $($field: $var,)* $next_field: $next_field } = $value);
53    };
54    (@tuple (); ($($index_rest:tt),*)
55        => ($($ty:tt),*); let ($($index:tt: $var:pat_param),*) = $value:expr
56    ) => {
57        let value: ($($ty,)*) = $value; // asserts correct arity
58        let value = ::core::mem::ManuallyDrop::new(value);
59        let value = $crate::__manually_drop_inner_ref(&value);
60        // SAFETY: We avoid double free by 1) only reading each field once (since they're unique) and 2) the original is wrapped in ManuallyDrop.
61        $(
62            let $var = unsafe { ::core::ptr::addr_of!(value.$index).read() };
63        )*
64    };
65    (@tuple ($var_head:pat_param $(,$var_tail:pat_param)*); ()
66        => ($($ty:tt),*); let ($($index:tt: $var:pat_param),*) = $value:expr
67    ) => {
68        compile_error!("tuple arity is larger than the maximum supported arity 12")
69    };
70    (@tuple ($var_head:pat_param $(,$var_tail:pat_param)*); ($index_head:tt $(,$index_tail:tt)*)
71        => ($($ty:tt),*); let ($($index:tt: $var:pat_param),*) = $value:expr
72    ) => {
73        $crate::const_destructure!(@tuple ($($var_tail),*); ($($index_tail),*) => ($($ty,)* _); let ($($index: $var,)* $index_head: $var_head) = $value);
74    };
75}