Skip to main content

const_destructure/
lib.rs

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