const_tools/
destructure.rs

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