Skip to main content

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:path {
6        $($field:ident $(: $field_alias:ident)?),* $(,)?
7    } = $value:expr) => {
8        let value: $ty = $value;
9
10        // errors if there are duplicates
11        #[allow(clippy::unneeded_wildcard_pattern)]
12        let $ty { $($field: _,)* .. } = &value;
13
14        let value = ::core::mem::ManuallyDrop::new(value);
15
16        $(
17            #[allow(unused_unsafe)] // we might or might not already be in an unsafe context
18            let $crate::destructure::or!($($field_alias)? $field) = unsafe { ::core::ptr::read(&value.$field) };
19        )*
20    };
21}
22
23pub(crate) use destructure;
24
25macro_rules! or {
26    ($this:ident $that:ident) => {
27        $this
28    };
29    ($that:ident) => {
30        $that
31    };
32}
33
34pub(crate) use or;
35
36#[cfg(test)]
37mod tests {
38    use std::string::String;
39
40    #[test]
41    fn example() {
42        pub struct Foo {
43            bar: String,
44            baz: String,
45        }
46
47        impl Drop for Foo {
48            fn drop(&mut self) {
49                unreachable!()
50            }
51        }
52
53        let foo = Foo {
54            bar: "bar".into(),
55            baz: "baz".into(),
56        };
57
58        // won't compile
59        // let Foo { bar: qux, baz } = foo;
60
61        // won't compile
62        // destructure!(let Foo { bar, bar } = foo);
63
64        destructure!(let Foo { bar: qux, baz } = foo);
65
66        assert_eq!(qux, "bar");
67        assert_eq!(baz, "baz");
68    }
69}