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