bump_scope/
destructure.rs

1/// Allows you to destructure structs that have a drop implementation.
2///
3/// The drop implementation will not be called for `$ty` nor for any field that is not bound.
4macro_rules! destructure {
5    (let $ty:ty {
6        $($field:ident $(: $field_alias:ident)?),* $(,)?
7    } = $value:expr) => {
8        let value: $ty = $value;
9        let value = ::core::mem::ManuallyDrop::new(value);
10
11        const _: () = assert!(!$crate::destructure::has_duplicates(&[$(stringify!($field)),*]), "you can't destructure a field twice");
12
13        $(
14            #[allow(unused_unsafe)] // we might or might not already be in an unsafe context
15            let $crate::destructure::or!($($field_alias)? $field) = unsafe { ::core::ptr::read(&value.$field) };
16        )*
17    };
18}
19
20pub(crate) use destructure;
21
22macro_rules! or {
23    ($this:ident $that:ident) => {
24        $this
25    };
26    ($that:ident) => {
27        $that
28    };
29}
30
31pub(crate) use or;
32
33pub(crate) const fn has_duplicates(strings: &[&str]) -> bool {
34    let mut x = 0;
35
36    while x < strings.len() {
37        let mut y = x + 1;
38
39        while y < strings.len() {
40            if str_eq(strings[x], strings[y]) {
41                return true;
42            }
43
44            y += 1;
45        }
46
47        x += 1;
48    }
49
50    false
51}
52
53const fn str_eq(a: &str, b: &str) -> bool {
54    let a = a.as_bytes();
55    let b = b.as_bytes();
56
57    if a.len() != b.len() {
58        return false;
59    }
60
61    let mut i = 0;
62
63    while i < a.len() {
64        if a[i] != b[i] {
65            return false;
66        }
67
68        i += 1;
69    }
70
71    true
72}
73
74#[cfg(test)]
75mod tests {
76    use std::string::String;
77
78    #[test]
79    fn example() {
80        pub struct Foo {
81            bar: String,
82            baz: String,
83        }
84
85        impl Drop for Foo {
86            fn drop(&mut self) {
87                unreachable!()
88            }
89        }
90
91        let foo = Foo {
92            bar: "bar".into(),
93            baz: "baz".into(),
94        };
95
96        // won't compile
97        // let Foo { bar: qux, baz } = foo;
98
99        destructure!(let Foo { bar: qux, baz } = foo);
100
101        assert_eq!(qux, "bar");
102        assert_eq!(baz, "baz");
103    }
104}