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_struct {
10    (let $S:ident { $($field:ident: $var:ident),* } = $value:expr) => {
11        let value = $value;
12        let __destructures_all_fields_and_fields_are_unique = || {
13            let $S { $($field: _),* } = &value;
14        };
15        let value = ::core::mem::ManuallyDrop::new($value);
16        let value = $crate::__manually_drop_inner_ref(&value);
17        // SAFETY: We avoid double free by 1) only reading each field once (since they're unique) and 2) the original is wrapped in ManuallyDrop.
18        $(
19            let $var = unsafe { ::core::ptr::addr_of!(value.$field).read() };
20        )*
21    }
22}
23
24#[macro_export]
25macro_rules! const_destructure_tuple {
26    (let ($($var:ident),*) = $value:expr) => {
27        $crate::const_destructure_tuple!(@impl ($($var),*); (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) => (); let () = $value);
28    };
29    (@impl (); ($($index_rest:tt),*) => ($($ty:tt),*); let ($($index:tt: $var:ident),*) = $value:expr) => {
30        let value: ($($ty,)*) = $value; // asserts correct arity
31        let value = ::core::mem::ManuallyDrop::new(value);
32        let value = $crate::__manually_drop_inner_ref(&value);
33        // SAFETY: We avoid double free by 1) only reading each field once (since they're unique) and 2) the original is wrapped in ManuallyDrop.
34        $(
35            let $var = unsafe { ::core::ptr::addr_of!(value.$index).read() };
36        )*
37    };
38    (@impl ($var_head:ident $(,$var_tail:ident)*); () => ($($ty:tt),*); let ($($index:tt: $var:ident),*) = $value:expr) => {
39        compile_error!("tuple arity is larger than the maximum supported arity 12")
40    };
41    (@impl ($var_head:ident $(,$var_tail:ident)*); ($index_head:tt $(,$index_tail:tt)*) => ($($ty:tt),*); let ($($index:tt: $var:ident),*) = $value:expr) => {
42        $crate::const_destructure_tuple!(@impl ($($var_tail),*); ($($index_tail),*) => ($($ty,)* _); let ($($index: $var,)* $index_head: $var_head) = $value);
43    };
44}
45
46// const fn s() {
47//     struct S<A, B>{ a: A, b: B}
48//     const_destructure_struct!(let S { a: da, b: db } = S { a: 1, b: [0u8; 1] });
49// }
50
51// const fn x() {
52//     let v = (1, 2);
53//     const_destructure_tuple!(let (t0, t1) = v);
54//     if t0 != 1 || t1 != 2 {
55//         panic!()
56//     }
57// }
58// const X: () = x();
59
60// const fn x13() {
61//     let v = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11);
62//     const_destructure_tuple!(let (t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) = v);
63// }
64
65// const X13: () = x13();
66
67// const fn too_many() {
68//     let v = (0, 1);
69//     const_destructure_tuple!(let (t0, t1, t2) = v);
70// }
71
72// const fn too_few() {
73//     let v = (0, 1);
74//     const_destructure_tuple!(let (t0) = v);
75// }
76
77// macro_rules! const_map {
78//     ($N:tt => ($($n:ident in $a:expr),+ $(,)?) -> ($($o:ident $ot:ty => $oa:ident),+ $(,)?) $body:expr) => {{
79//         $(
80//             let mut $o = array_uninit::<$ot, $N>();
81//         )*
82
83//         $(
84//             #[allow(clippy::redundant_locals)]
85//             let $n: [_; $N] = $a;
86//             let $n = core::mem::ManuallyDrop::new($n);
87//             let $n = manually_drop_inner_ref(&$n);
88//         )*
89
90//         const_for!{index in 0..$N => {
91//             $(
92//                 let $n = unsafe { core::ptr::addr_of!($n[index]).read() };
93//             )*
94//             let item: ($($ot),*) = $body;
95//             const_destructure!(let $($o: ))
96//             $(
97//                 $o[index].write()
98//             )
99//             out[index].write(item);
100//         }};
101
102//         unsafe { array_assume_init(out) }
103//     }};
104// }
105// pub(crate) use const_map;