pub trait Nullable {
const NULL: Self;
fn is_null(&self) -> bool;
}
macro_rules! impl_nullable {
($type:ty) => {
impl_nullable!($type, <>);
};
($type:ty, <$($letter:ident),*>) => {
impl<$($letter)*> Nullable for $type {
const NULL: Self = 0 as $type;
#[inline]
fn is_null(&self) -> bool {
*self == Self::NULL
}
}
};
}
macro_rules! impl_nullables {
($($type:ty),*) => {
$(
impl_nullable!($type);
)*
}
}
impl_nullable!(*const T, <T>);
impl_nullable!(*mut T, <T>);
impl_nullables!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize);
impl<T> Nullable for Option<T> {
const NULL: Self = None;
#[inline]
fn is_null(&self) -> bool {
self.is_none()
}
}
impl Nullable for () {
const NULL: Self = ();
#[inline]
fn is_null(&self) -> bool {
true
}
}
#[macro_export]
macro_rules! null_pointer_check {
($ptr:expr) => {
null_pointer_check!($ptr, Nullable::NULL)
};
($ptr:expr, $null:expr) => {{
#[allow(unused_imports)]
use $crate::Nullable;
if <_ as $crate::Nullable>::is_null(&$ptr) {
$crate::error_handling::update_last_error($crate::NullPointer);
return $null;
}
}};
}
#[derive(Debug, Copy, Clone, PartialEq, Fail)]
#[fail(display = "A null pointer was passed in where it wasn't expected")]
pub struct NullPointer;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn null_pointer_check_on_garbage_address_doesnt_segfault() {
let random_address = 12345 as *const u8;
assert!(!random_address.is_null());
}
#[test]
fn can_detect_null_pointers() {
let null = 0 as *const u8;
assert!(<_ as Nullable>::is_null(&null));
}
#[test]
fn can_detect_non_null_pointers() {
let thing = 123;
let not_null = &thing as *const i32;
assert!(!<_ as Nullable>::is_null(¬_null));
}
}