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