konst/macros/
destructuring.rs

1#![expect(non_camel_case_types)]
2#![expect(clippy::empty_loop)]
3
4use core::marker::PhantomData;
5use core::mem::ManuallyDrop;
6
7//////
8
9#[doc(hidden)]
10pub trait __GetImplsHelper {
11    type T: ?Sized;
12
13    fn __impls_drop_iwrhqlpnnieu8c6w(&self) -> __DoesNotImplDrop<Self::T>;
14}
15
16//////
17
18#[doc(hidden)]
19pub struct __ImplsDrop<T: ?Sized>(PhantomData<fn(T) -> T>);
20
21//////
22
23#[doc(hidden)]
24pub struct __DoesNotImplDrop<T: ?Sized>(PhantomData<fn(T) -> T>);
25
26impl<T: ?Sized> __DoesNotImplDrop<T> {
27    pub const fn new(_: *mut T) -> Self {
28        Self(PhantomData)
29    }
30}
31
32//////
33
34#[doc(hidden)]
35pub struct __GetImpls_IWRHQLPNNIEU8C6W<T: ?Sized>(pub PhantomData<fn(T) -> T>);
36
37impl<T> __GetImplsHelper for __GetImpls_IWRHQLPNNIEU8C6W<T> {
38    type T = T;
39
40    fn __impls_drop_iwrhqlpnnieu8c6w(&self) -> __DoesNotImplDrop<Self::T> {
41        __DoesNotImplDrop(PhantomData)
42    }
43}
44
45#[expect(drop_bounds)]
46impl<T> __GetImpls_IWRHQLPNNIEU8C6W<T>
47where
48    // I really do mean to test for Drop,
49    // because it's to destructure a struct into its fields
50    T: Drop,
51{
52    pub fn __impls_drop_iwrhqlpnnieu8c6w(self) -> __ImplsDrop<T> {
53        __ImplsDrop(PhantomData)
54    }
55}
56
57//////
58
59#[doc(hidden)]
60#[inline(always)]
61pub const fn cast_manuallydrop_ptr<T>(ptr: *mut ManuallyDrop<T>) -> *mut T {
62    ptr.cast()
63}
64
65#[doc(hidden)]
66#[inline(always)]
67/// Gets a pointer to the first elem in a ManuallyDrop array
68pub const fn cast_manuallydrop_array_ptr<T, const N: usize>(
69    ptr: *mut ManuallyDrop<[T; N]>,
70) -> *mut T {
71    ptr.cast()
72}
73
74#[doc(hidden)]
75#[inline(always)]
76pub const fn cast_ptr_with_phantom<T, U>(ptr: *mut T, _phantom: PhantomData<fn(U) -> U>) -> *mut U {
77    ptr.cast()
78}
79
80#[doc(hidden)]
81#[inline(always)]
82pub const fn get_phantom_len<T, const N: usize>(
83    _phantom: PhantomData<fn([T; N]) -> [T; N]>,
84) -> usize {
85    N
86}
87
88#[doc(hidden)]
89#[inline(always)]
90pub const fn make_it<T>() -> T {
91    loop {}
92}
93
94#[doc(hidden)]
95#[inline(always)]
96pub const fn fake_read<T>(_: *mut T) -> T {
97    loop {}
98}
99
100#[doc(hidden)]
101#[inline(always)]
102pub const fn make_phantom<T>(_: *mut T) -> PhantomData<fn(T) -> T> {
103    PhantomData
104}
105
106#[doc(hidden)]
107#[inline(always)]
108pub const fn array_into_phantom<T, const N: usize>(
109    val: [T; N],
110) -> PhantomData<fn([T; N]) -> [T; N]> {
111    core::mem::forget(val);
112    PhantomData
113}
114
115#[doc(hidden)]
116#[inline(always)]
117pub const fn assert_same_type<T>(this: T, that: T) {
118    core::mem::forget(this);
119    core::mem::forget(that);
120}
121
122//////
123
124#[doc(hidden)]
125pub type __ArrayManuallyDrop<T, const LEN: usize> = ManuallyDrop<[T; LEN]>;
126
127//////
128
129/// Destructures a struct/tuple/array into all of its elements/fields.
130///
131/// [**for examples look here**](#examples)
132///
133/// # Motivation
134///
135/// This macro works around a limitation of Rust as of 1.89,
136/// where in a const context, a non-`Drop` type can't be destructured into its elements/fields
137/// if any of them is `Drop`.
138///
139/// Even simple cases like this don't compile:
140///
141/// ```rust,compile_fail
142/// const fn foo<T>((a, b): (T, T)) -> [T; 2] {
143///     [a, b]
144/// }
145/// ```
146///
147/// ```text
148/// error[E0493]: destructor of `(T, T)` cannot be evaluated at compile-time
149///  --> konst/src/macros/destructuring.rs:142:17
150///   |
151/// 3 | const fn foo<T>((a, b): (T, T)) -> [T; 2] {
152///   |                 ^^^^^^ the destructor for this type cannot be evaluated in constant functions
153/// 4 |     [a, b]
154/// 5 | }
155///   | - value is dropped here
156/// ```
157///
158/// # Requirements/Limitations
159///
160/// This macro has these requirements and limitations:
161/// - it does not support `..` patterns in tuples or structs
162///   (because unmentioned fields would be leaked),
163///   but `..` patterns are supported in arrays.
164/// - it requires that passed-in structs do not impl `Drop`
165///   (like built-in destructuring does),
166///   but any field can impl `Drop`.
167/// - it needs to be invoked multiple times
168///   to destructure nested structs/tuples/arrays that have `Drop` elements/fields.
169///   [(example)](#nested-destructuring)
170/// - it only supports tuple structs and tuples up to 16 elements (inclusive)
171///
172/// # Syntax
173///
174/// This section uses a pseudo-macro_rules syntax for each type of input.
175///
176/// ### Braced structs
177///
178/// ```text
179/// $struct_path:brace_path {$($field:tt $(: $pattern:pat)?),* $(,)?}
180/// $(:$struct_ty:ty)?
181/// = $val:expr
182/// ```
183///
184/// Where `:brace_path` can be either of:
185/// - `$(::)? $($path:ident)::* $(,)?`
186/// - `$struct_path:path $(,)?`
187///
188/// [example below](#braced-struct)
189///
190/// ### Tuple structs
191///
192/// ```text
193/// $struct_path:tuple_path ( $($pattern:pat),* $(,)? )
194/// $(:$struct_ty:ty)?
195/// = $val:expr
196/// ```
197///
198/// Where `:tuple_path` can be either of:
199/// - `$(::)? $($path:ident)::* $(,)?`
200/// - `$struct_path:path ,` (braced struct patterns don't need the `,`)
201///
202/// [example below](#tuple-struct)
203///
204/// ### Tuples
205///
206/// ```text
207/// ( $($pattern:pat),* $(,)? ) $(:$tuple_ty:ty)? = $val:expr
208/// ```
209/// [example below](#tuple)
210///
211/// ### Arrays
212///
213/// ```text
214/// [$( $pat:elem_pat $(@ ..)? ),* $(,)?] $(:$array_ty:ty)? = $val:expr
215///
216/// Where `:elem_pat` can be any of:
217/// - `_`
218/// - `..`
219/// - `$ident:ident`
220/// - `($pattern:pat)`: any pattern inside of parentheses
221/// ```
222/// [example below](#array)
223///
224/// # Examples
225///
226/// These examples demonstrate destructuring non-Copy types in const,
227/// which can't be done with built-in destructuring as of Rust 1.89.
228///
229/// ### Braced Struct
230///
231/// ```rust
232/// use std::ops::Range;
233///
234/// assert_eq!(PAIR, (3, 5));
235///
236/// const PAIR: (u32, u32) = range_to_pair(3..5);
237///
238/// const fn range_to_pair<T>(range: Range<T>) -> (T, T) {
239///     konst::destructure!{Range{start, end} = range}
240///
241///     (start, end)
242/// }
243/// ```
244///
245/// ### Tuple Struct
246///
247/// ```rust
248///
249/// assert_eq!(PAIR, [8, 13]);
250///
251/// const PAIR: [u32; 2] = Pair(8, 13).into_inner();
252///
253/// struct Pair<T>(T, T);
254///
255/// impl<T> Pair<T> {
256///     const fn into_inner(self) -> [T; 2] {
257///         konst::destructure!{Self(first, second) = self}
258///     
259///         [first, second]
260///     }
261/// }
262/// ```
263///
264/// ### Tuple
265///
266/// ```rust
267///
268/// assert_eq!(PAIR, (5, String::new()));
269///
270/// const PAIR: (u32, String) = swap_pair((String::new(), 5));
271///
272/// const fn swap_pair<T, U>(pair: (T, U)) -> (U, T) {
273///     konst::destructure!{(a, b) = pair}
274///
275///     (b, a)
276/// }
277/// ```
278///
279/// ### Array
280///
281/// ```rust
282///
283/// assert_eq!(SPLIT, (Some(String::new()), [None, None, Some(String::new())]));
284///
285/// const SPLIT: (Option<String>, [Option<String>; 3]) =
286///     split_first([Some(String::new()), None, None, Some(String::new())]);
287///
288/// const fn split_first<T>(array: [T; 4]) -> (T, [T; 3]) {
289///     konst::destructure!{[a, rem @ ..] = array}
290///     
291///     (a, rem)
292/// }
293/// ```
294///
295/// ### Nested Destructuring
296///
297/// ```rust
298///
299/// assert_eq!(TRIPLE, [3, 5, 8]);
300///
301/// const TRIPLE: [u8; 3] = flatten((3, (5, 8)));
302///
303/// const fn flatten<T>(tup: (T, (T, T))) -> [T; 3] {
304///     // `tail` can't be destructured inline into `(b, c)`,
305///     // it must be destructured separately
306///     konst::destructure!{(a, tail) = tup}
307///     
308///     konst::destructure!{(b, c) = tail}
309///
310///     [a, b, c]
311/// }
312///
313///
314#[macro_export]
315macro_rules! destructure {
316    // braced struct struct
317    ($($(@$is_path:tt)? ::)? $($path:ident)::+ $(,)?{$($braced:tt)*} $($rem:tt)*) => (
318        $crate::__destructure__braced_struct_prepare_fields! {
319            {$($braced)*}
320            ($($(@$is_path)? ::)? $($path)::*)
321            path
322            $($rem)*
323        }
324    );
325
326    // tuple struct
327    ($($(@$is_path:tt)? ::)? $($path:ident)::+ $(,)? ($($tupled:tt)*) $($rem:tt)*) => (
328        $crate::__destructure__tuple_struct_field_names!{
329            (
330                ($($(@$is_path)? ::)? $($path)::+)
331                path
332                $($rem)*
333            )
334            ()
335            ($($tupled)*)
336            (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
337        }
338    );
339
340    // braced struct struct
341    ($struct_path:path $(,)? {$($braced:tt)*} $($rem:tt)*) => (
342        $crate::__destructure__braced_struct_prepare_fields! {
343            {$($braced)*}
344            ($struct_path)
345            type
346            $($rem)*
347        }
348    );
349
350    // tuple struct
351    ($struct_path:path, ($($tupled:tt)*) $($rem:tt)*) => (
352        $crate::__destructure__tuple_struct_field_names!{
353            (
354                ($struct_path)
355                type
356                $($rem)*
357            )
358            ()
359            ($($tupled)*)
360            (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
361        }
362    );
363
364    // tuple
365    (() $(:$tuple_ty:ty)? = $val:expr) => (
366        let () $(: $tuple_ty)? = $val;
367    );
368    (($($tupled:tt)*) $($rem:tt)*) => (
369        $crate::__destructure__tuple_field_names!{
370            ($($rem)*)
371            ()
372            ($($tupled)*)
373            (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
374        }
375    );
376
377    // array
378    ([] $(:$array_ty:ty)? = $val:expr) => (
379        let [] $(: $array_ty)? = $val;
380    );
381
382    (
383        [$( $pat:tt $(@ $dotdot:tt)? ),* $(,)?]
384
385        $($rem:tt)*
386    ) => (
387        $crate::__destructure_array__process_fields!{
388            [$( ($pat) ($($dotdot $dotdot)?) ,)*]
389
390            $($rem)*
391        }
392    );
393}
394
395#[doc(hidden)]
396#[macro_export]
397macro_rules! __destructure__braced_struct_prepare_fields {
398    (
399        {$($field:tt $(: $pattern:pat)?),* $(,)?}
400        $($rem:tt)*
401    ) => {
402        $crate::__destructure_struct!{
403            {$(($field) $field $(: $pattern)?,)*}
404
405            $($rem)*
406        }
407    }
408}
409
410#[doc(hidden)]
411#[macro_export]
412macro_rules! __destructure__tuple_struct_field_names {
413    (($($rem:tt)*) ($($patterns:tt)*) () ($($fnames:tt)*)) => {
414        $crate::__destructure_struct!{
415            {$($patterns)*}
416            $($rem)*
417        }
418    };
419    ($fixed:tt $prev_patterns:tt (.. $($next_pattern:tt)*) $fnames:tt) => {
420        $crate::__::compile_error!{
421            "`..` patterns are not supported in top-level tuple struct patterns"
422        }
423    };
424    (
425        $fixed:tt
426        ($($prev_patterns:tt)*)
427        ($pattern:pat $(, $($next_pattern:tt)*)?)
428        ($fname:tt $($next_fnames:tt)*)
429    ) => {
430        $crate::__destructure__tuple_struct_field_names!{
431            $fixed
432            ($($prev_patterns)* ($fname) $fname:$pattern,)
433            ($($($next_pattern)*)?)
434            ($($next_fnames)*)
435        }
436    };
437}
438
439#[doc(hidden)]
440#[macro_export]
441macro_rules! __destructure_struct {
442    (
443        { /* no fields */ $(,)? }
444        ($($struct_path:tt)*)
445        $path_kind:ident
446        $(:$struct_ty:ty)?
447        = $val:expr
448    ) => (
449        let $($struct_path)* {} $(: $struct_ty)? = $val;
450    );
451    (
452        {
453            $(($($_fa0:ident)? $($_fa1:literal)?) $field:tt $(: $pattern:pat)?),*
454            $(,)?
455        }
456        ($($struct_path:tt)*)
457        $path_kind:ident
458        $(:$struct_ty:ty)?
459        = $val:expr
460    ) => (
461        // assert that `$struct_path` has precisely the fields that the user listed
462        let val @ $($struct_path)* {$($field: _),*} $(: $struct_ty)? = $val;
463
464        // asserts that `val` is not a reference,
465        // protects against match ergonomics allowing `$val` to be a references.
466        //
467        // This always uses `$($struct_path)*`, even if `$struct_ty` is passed,
468        // because:
469        // - if this tested the type using `$struct_ty`,
470        //   it would allow passing a reference to a struct when `$struct_ty == &Struct`.
471        // - `$($struct_path)*` is guaranteed to be a struct due to it being used
472        //   in the pattern above
473        $crate::__destructuring__type_assert!{($path_kind $($struct_path)*) val}
474
475        let mut val = $crate::__::ManuallyDrop::new(val);
476
477        let ptr: *mut _ = $crate::macros::destructuring::cast_manuallydrop_ptr(&raw mut val);
478
479
480        if false {
481            _ = ||{
482                use $crate::macros::destructuring::__GetImplsHelper as _;
483
484                // assert that the struct doesn't impl Drop
485                // (its fields can, just not the struct itself)
486                let _assertion_expected: $crate::macros::destructuring::__DoesNotImplDrop<_> =
487                    if false {
488                        $crate::macros::destructuring::__DoesNotImplDrop::new(ptr)
489                    } else {
490                        let assertion = $crate::macros
491                            ::destructuring
492                            ::__GetImpls_IWRHQLPNNIEU8C6W(
493                                $crate::macros::destructuring::make_phantom(ptr)
494                            ).__impls_drop_iwrhqlpnnieu8c6w();
495
496                        assertion
497                    };
498            };
499        }
500
501
502        $(
503            // SAFETY: the value being wrapped in a ManuallyDrop,
504            //         and the asserts above, ensure that these reads are safe.
505            //
506            // using `read_unaligned` to support destructuring packed structs
507            let $crate::__first_pat!( $($pattern,)? $field, ) = unsafe {
508                $crate::__::ptr::read_unaligned(&raw mut (*ptr).$field)
509            };
510        )*
511    );
512    (
513        {
514            $(($($_fa0:ident)? $($_fa1:literal)?) $field0:tt $(: $_0:pat)? ,)*
515            $(
516                (..) $field1:tt $(: $_1:pat)? ,
517                $($__anything:tt)*
518            )?
519        }
520        $($_3:tt)*
521    ) => (
522        compile_error!{"`..` patterns are not supported in top-level struct patterns"}
523    )
524}
525
526#[doc(hidden)]
527#[macro_export]
528macro_rules! __destructuring__type_assert {
529    ((path $($path:tt)*) $variable:ident) => {
530        // assert that `$variable` is a struct, not a reference to a struct
531        #[allow(unreachable_code)]
532        if false {
533            loop {}
534
535            let expected @ $($path)* {..};
536
537            $crate::macros::destructuring::assert_same_type(expected, $variable)
538        }
539    };
540    ((type $type:ty) $variable:ident) => {
541        let _: $type = $variable;
542    };
543}
544
545#[doc(hidden)]
546#[macro_export]
547macro_rules! __destructure__tuple_field_names {
548    (($($rem:tt)*) ($($patterns:tt)*) () ($($fnames:tt)*)) => {
549        $crate::__destructure_tuple!{($($patterns)*) $($rem)*}
550    };
551    ($fixed:tt $prev_patterns:tt (.. $($next_pattern:tt)*) $fnames:tt) => {
552        $crate::__::compile_error!{"`..` patterns are not supported in top-level tuple patterns"}
553    };
554    (
555        $fixed:tt
556        ($($prev_patterns:tt)*)
557        ($pattern:pat $(, $($next_pattern:tt)*)?)
558        ($fname:tt $($next_fnames:tt)*)
559    ) => {
560        $crate::__destructure__tuple_field_names!{
561            $fixed
562            ($($prev_patterns)* $fname:$pattern,)
563            ($($($next_pattern)*)?)
564            ($($next_fnames)*)
565        }
566    };
567}
568
569#[doc(hidden)]
570#[macro_export]
571macro_rules! __destructure_tuple {
572    (($($field:tt: $pattern:pat,)*) $(:$tuple_ty:ty)? = $val:expr) => (
573
574        // assert that the tuple has precisely the element count that the user passed
575        //
576        // $tuple_ty being passed is not enough of an assertion,
577        // because it might be a tuple with more elements,
578        // so we construct a pattern to assert it.
579        let val @ ($($crate::__first_pat!(_, $field),)*)
580            : $crate::__first_ty!($($tuple_ty,)? ($($crate::__first_ty!(_, $field),)*),)
581            = $val;
582
583        // assert that `val` is a tuple, not a reference to a tuple
584        #[allow(unreachable_code)]
585        if false {
586            loop {}
587
588            let expected @ ($($crate::__first_pat!(_, $field),)*);
589
590            $crate::macros::destructuring::assert_same_type(expected, val)
591        }
592
593        let mut val = $crate::__::ManuallyDrop::new(val);
594
595        let ptr = $crate::macros::destructuring::cast_manuallydrop_ptr(&raw mut val);
596
597        $(
598            // SAFETY: the value being wrapped in a ManuallyDrop,
599            //         and the asserts above, ensure that these reads are safe.
600            let $pattern = unsafe { $crate::__::ptr::read(&raw mut (*ptr).$field) };
601        )*
602    )
603}
604
605#[doc(hidden)]
606#[macro_export]
607macro_rules! __destructure_array__process_fields {
608    (
609        [$(
610            (
611                $( _ $($is_underscore:lifetime)? )?
612                $( $ident:ident)?
613                $( ($parenthesized:pat) )?
614                $( .. $($has_rem_elems1:lifetime)? )?
615            )
616            ($(.. $rem_elems:tt $($has_rem_elems2:lifetime)?)?),
617        )*]
618
619        $($rem:tt)*
620    ) => {
621        $crate::__destructure_array! {
622            [$(
623                (
624                    (
625                        $(rem $($has_rem_elems1)?)?
626                        $(rem $($has_rem_elems2)?)?
627                        elem
628                    )
629
630                    $(_ $($is_underscore)?)?
631                    $(_ $($has_rem_elems1)?)?
632                    $( $ident )?
633                    $( $parenthesized )?
634                )
635            )*]
636
637            $($rem)*
638        }
639    }
640}
641
642#[doc(hidden)]
643#[macro_export]
644macro_rules! __destructure_array {
645    (
646        [
647            $( ((elem $($__0:tt)*) $pat_prefix:tt) )*
648            $(
649                ((rem $($__1:tt)*) $pat_rem:tt)
650
651                $( ((elem $($__2:tt)*) $pat_suffix:tt) )*
652            )?
653        ]
654
655        $(:$array_ty:ty)?
656        = $val:expr
657    ) => {
658        let array $(: $array_ty)? = $val;
659
660        $(  let $crate::__first_pat!(rem_ty_phantom, $pat_rem) = $crate::__::PhantomData; )?
661
662        // asserts the length of the array,
663        // and computes the length of the array produced by `@ ..` patterns
664        #[allow(unreachable_code)]
665        if false {
666            loop {}
667
668            // assert that `array` is an array, not a reference to an array
669            _ = $crate::macros::destructuring::array_into_phantom(array);
670
671            let [
672                $($crate::__first_pat!(_, $pat_prefix),)*
673                $(
674                    $crate::__first_pat!(rem @ .., $pat_rem),
675                    $($crate::__first_pat!(_, $pat_suffix),)*
676                )?
677            ] = array;
678
679            $(
680                rem_ty_phantom = $crate::macros::destructuring::array_into_phantom(
681                    $crate::__first_expr!(rem, $pat_rem)
682                );
683            )?
684
685        }
686
687        let mut array = $crate::macros::destructuring::__ArrayManuallyDrop::new(array);
688
689        let ptr = $crate::macros::destructuring::cast_manuallydrop_array_ptr(&raw mut array);
690        let mut i = 0;
691
692
693        $crate::__destructure_array__read_elems!{unsafe, ptr, i, [$($pat_prefix),*]}
694
695        $(
696
697            // SAFETY: the array being wrapped in a ManuallyDrop,
698            //         and the assertions above, ensure that these reads are safe.
699            let $pat_rem = unsafe {
700                let rem_ptr = $crate::macros::destructuring::cast_ptr_with_phantom(
701                    <*mut _>::add(ptr, i),
702                    rem_ty_phantom,
703                );
704
705                $crate::__::ptr::read(rem_ptr)
706            };
707
708            i += $crate::macros::destructuring::get_phantom_len(rem_ty_phantom);
709
710
711            $crate::__destructure_array__read_elems!{unsafe, ptr, i, [$($pat_suffix),*]}
712        )?
713    }
714}
715
716#[doc(hidden)]
717#[macro_export]
718macro_rules! __destructure_array__read_elems {
719    ($unsafe:ident, $ptr:ident, $i:ident, [$($pattern:pat),*]) => {
720        $(
721            // SAFETY: the array being wrapped in a ManuallyDrop,
722            //         and the assertions above, ensure that these reads are safe.
723            let $pattern = $unsafe { $crate::__::ptr::read(<*mut _>::add($ptr, $i)) };
724
725            $i += 1;
726        )*
727    }
728}
729
730#[macro_export]
731#[doc(hidden)]
732macro_rules! __first_pat {
733    ($first:pat, $($rem:tt)* ) => {
734        $first
735    };
736}
737
738#[macro_export]
739#[doc(hidden)]
740macro_rules! __first_ty {
741    ($first:ty, $($rem:tt)* ) => {
742        $first
743    };
744}
745
746#[macro_export]
747#[doc(hidden)]
748macro_rules! __first_expr {
749    ($first:expr, $($rem:tt)* ) => {
750        $first
751    };
752}