Skip to main content

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/// Allows destructuring in const contexts.
8///
9/// # Examples
10///
11/// Destructuring tuples:
12///
13/// ```
14/// use const_tools::destructure;
15///
16/// const fn swap<A, B>(value: (A, B)) -> (B, A) {
17///     destructure!(let (a, b) = value);
18///     (b, a)
19/// }
20/// ```
21///
22/// Destructuring structs:
23///
24/// ```
25/// use const_tools::destructure;
26///
27/// struct Pair<A, B> {
28///     first: A,
29///     second: B,
30/// }
31///
32/// impl<A, B> Pair<A, B> {
33///     const fn swap(self) -> Pair<B, A> {
34///         destructure!(let Self { first, second } = self);
35///         Pair { first: second, second: first }
36///     }
37/// }
38/// ```
39#[macro_export]
40macro_rules! destructure {
41    (let $S:path { $($field_spec:tt)* } = $value:expr) => {
42        $crate::__destructure__struct!(($($field_spec)*) => let $S {} = $value);
43    };
44    (let ($($var:pat_param),* $(,)?) = $value:expr) => {
45        $crate::__destructure__tuple!(($($var),*); (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) => (); let () = $value);
46    };
47}
48
49#[doc(hidden)]
50#[macro_export]
51macro_rules! __destructure__struct {
52    (()
53        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
54    ) => {
55        let value = $value;
56        let value = ::core::mem::ManuallyDrop::new(value);
57        let value = $crate::__manually_drop_inner_ref(&value);
58        let __assert_valid_destructure = || {
59            #[allow(unused)]
60            let $S { $($field: $var),* } = $crate::__unimplemented_to_owned(value);
61        };
62        // 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.
63        $(
64            let $var = unsafe { ::core::ptr::addr_of!(value.$field).read() };
65        )*
66    };
67    (($next_field:ident: $next_var:pat_param $(,)?)
68        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
69    ) => {
70        $crate::__destructure__struct!(() => let $S { $($field: $var,)* $next_field: $next_var } = $value);
71    };
72    (($next_field:ident $(,)?)
73        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
74    ) => {
75        $crate::__destructure__struct!(() => let $S { $($field: $var,)* $next_field: $next_field } = $value);
76    };
77    (($next_field:ident: $next_var:pat_param, $($rest:tt)*)
78        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
79    ) => {
80        $crate::__destructure__struct!(($($rest)*) => let $S { $($field: $var,)* $next_field: $next_var } = $value);
81    };
82    (($next_field:ident, $($rest:tt)*)
83        => let $S:path { $($field:ident: $var:pat_param),* } = $value:expr
84    ) => {
85        $crate::__destructure__struct!(($($rest)*) => let $S { $($field: $var,)* $next_field: $next_field } = $value);
86    };
87}
88
89#[doc(hidden)]
90#[macro_export]
91macro_rules! __destructure__tuple {
92    ((); ($($index_rest:tt),*)
93        => ($($ty:tt),*); let ($($index:tt: $var:pat_param),*) = $value:expr
94    ) => {
95        let value: ($($ty,)*) = $value; // asserts correct arity
96        let value = ::core::mem::ManuallyDrop::new(value);
97        let value = $crate::__manually_drop_inner_ref(&value);
98        // SAFETY: We avoid double free by 1) only reading each field once (since they're unique) and 2) the original is wrapped in ManuallyDrop.
99        $(
100            let $var = unsafe { ::core::ptr::addr_of!(value.$index).read() };
101        )*
102    };
103    (($var_head:pat_param $(,$var_tail:pat_param)*); ()
104        => ($($ty:tt),*); let ($($index:tt: $var:pat_param),*) = $value:expr
105    ) => {
106        compile_error!("tuple arity is larger than the maximum supported arity 12")
107    };
108    (($var_head:pat_param $(,$var_tail:pat_param)*); ($index_head:tt $(,$index_tail:tt)*)
109        => ($($ty:tt),*); let ($($index:tt: $var:pat_param),*) = $value:expr
110    ) => {
111        $crate::__destructure__tuple!(($($var_tail),*); ($($index_tail),*) => ($($ty,)* _); let ($($index: $var,)* $index_head: $var_head) = $value);
112    };
113}