const_zero/
lib.rs

1#![no_std]
2
3#[doc(hidden)]
4pub use core;
5
6/// A marco that acts similarly to [`core::mem::zeroed()`], only is const
7///
8/// Example usage:
9/// ```rust
10/// use const_zero::const_zero;
11/// struct OpaqueStruct {
12///     nothing: core::ffi::c_void,
13/// };
14/// static mut zeroed_opaque: OpaqueStruct = unsafe {const_zero!(OpaqueStruct)};
15/// ```
16///
17/// Ideally const_zero would be a generic function, but const generics need
18/// more development first (`const_fn_transmute`, `const_generics`,
19/// `const_evaluatable_checked`)
20///
21/// ## Safety
22/// Similar to `core::mem::zeroed()`, except this zeroes padding bits. Zeroed
23/// padding usually isn't relevant to safety, but might be if a C union is used.
24/// To repeat `core::mem::zeroed()`'s safety, an all zero byte pattern might not
25/// be a valid value for a type; for example, references &T, &mut T.
26///
27/// const_zero does not work on unsized types
28/// ```rust compile_fail
29/// use const_zero::const_zero;
30/// // error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
31/// const BYTES: [u8] = unsafe{const_zero!([u8])};
32/// ```
33/// reference types trigger a (denied by default) lint and cause immediate
34/// undefined behavior if the lint is ignored
35/// ```rust compile_fail
36/// use const_zero::const_zero;
37/// // error: any use of this value will cause an error
38/// // note: `#[deny(const_err)]` on by default
39/// const STR: &str = unsafe{const_zero!(&'static str)};
40/// ```
41///
42/// ## Differences with `core::mem::zeroed`
43/// `const_zero` zeroes padding bits, while `core::mem::zeroed` doesn't
44#[macro_export]
45macro_rules! const_zero {
46    ($type_:ty) => {{
47        const TYPE_SIZE: $crate::core::primitive::usize = $crate::core::mem::size_of::<$type_>();
48        union TypeAsBytes {
49            bytes: [$crate::core::primitive::u8; TYPE_SIZE],
50            inner: $crate::core::mem::ManuallyDrop<$type_>,
51        }
52        const ZERO: TypeAsBytes = TypeAsBytes {
53            bytes: [0; TYPE_SIZE],
54        };
55        $crate::core::mem::ManuallyDrop::<$type_>::into_inner(ZERO.inner)
56    }};
57}
58
59#[cfg(test)]
60mod tests {
61    use core::num::NonZeroU8;
62    use core::num::Wrapping;
63
64    // Ensure macros are hygienic, don't create name conflicts
65    #[test]
66    fn multiple() {
67        const ZERO_1: i32 = unsafe { const_zero!(i32) };
68        const ZERO_2: i32 = unsafe { const_zero!(i32) };
69        const ZERO_3: i64 = unsafe { const_zero!(i64) };
70        assert_eq!(ZERO_1, 0);
71        assert_eq!(ZERO_2, 0);
72        assert_eq!(ZERO_3, 0);
73    }
74
75    // All integers can be constructed
76    #[test]
77    fn zeroed_int() {
78        macro_rules! test_int {
79            ($type_:ty, $zero:expr) => {{
80                const ZERO: $type_ = unsafe { const_zero!($type_) };
81                assert_eq!(ZERO, $zero);
82            }};
83        }
84        test_int!(i8, 0);
85        test_int!(i16, 0);
86        test_int!(i32, 0);
87        test_int!(i64, 0);
88        test_int!(i128, 0);
89        test_int!(isize, 0);
90        test_int!(Wrapping<i8>, Wrapping(0));
91        test_int!(Wrapping<i16>, Wrapping(0));
92        test_int!(Wrapping<i32>, Wrapping(0));
93        test_int!(Wrapping<i64>, Wrapping(0));
94        test_int!(Wrapping<i128>, Wrapping(0));
95        test_int!(Wrapping<isize>, Wrapping(0));
96
97        test_int!(u8, 0);
98        test_int!(u16, 0);
99        test_int!(u32, 0);
100        test_int!(u64, 0);
101        test_int!(u128, 0);
102        test_int!(usize, 0);
103        test_int!(Wrapping<u8>, Wrapping(0));
104        test_int!(Wrapping<u16>, Wrapping(0));
105        test_int!(Wrapping<u32>, Wrapping(0));
106        test_int!(Wrapping<u64>, Wrapping(0));
107        test_int!(Wrapping<u128>, Wrapping(0));
108        test_int!(Wrapping<usize>, Wrapping(0));
109
110        test_int!(f32, 0.);
111        test_int!(f64, 0.);
112    }
113
114    #[test]
115    fn zeroed_ptr() {
116        const NULL: *const () = unsafe { const_zero!(*const ()) };
117        assert_eq!(NULL, core::ptr::null());
118        const NULL_MUT: *mut () = unsafe { const_zero!(*mut ()) };
119        assert_eq!(NULL_MUT, core::ptr::null_mut());
120    }
121
122    // sentinel value Option optimization works
123    #[test]
124    fn zeroed_option() {
125        const NONE: Option<NonZeroU8> = unsafe { const_zero!(Option<NonZeroU8>) };
126        assert_eq!(NONE, None);
127    }
128
129    // a type with a drop implementation works
130    #[test]
131    fn drop_type() {
132        #[derive(Clone, Debug)]
133        struct Droppable {}
134        impl Drop for Droppable {
135            fn drop(&mut self) {
136                // no-op
137            }
138        }
139        #[allow(unused)]
140        const DROPPABLE: Droppable = unsafe { const_zero!(Droppable) };
141    }
142
143    #[test]
144    fn zeroed_unit() {
145        const UNIT: () = unsafe { const_zero!(()) };
146        assert_eq!((), UNIT);
147    }
148}
149
150#[cfg(test)]
151mod test_no_implicit_prelude {
152    #![no_implicit_prelude]
153
154    #[test]
155    fn zeroed_unit() {
156        const UNIT: () = unsafe { const_zero!(()) };
157        ::core::assert_eq!((), UNIT);
158    }
159}