extendr_api/
na.rs

1use extendr_ffi::{R_IsNA, R_NaReal};
2use once_cell::sync::Lazy;
3use std::alloc::{self, Layout};
4
5// To make sure this "NA" is allocated at a different place than any other "NA"
6// strings (so that it can be used as a sentinel value), we allocate it by
7// ourselves.
8static EXTENDR_NA_STRING: Lazy<&'static str> = Lazy::new(|| unsafe {
9    // Layout::array() can fail when the size exceeds `isize::MAX`, but we
10    // only need 2 here, so it's safe to unwrap().
11    let layout = Layout::array::<u8>(2).unwrap();
12
13    // We allocate and never free it because we need this pointer to be
14    // alive until the program ends.
15    let ptr = alloc::alloc(layout);
16
17    let v: &mut [u8] = std::slice::from_raw_parts_mut(ptr, 2);
18    v[0] = b'N';
19    v[1] = b'A';
20
21    std::str::from_utf8_unchecked(v)
22});
23
24/// Return true if this primitive is `NA`.
25pub trait CanBeNA {
26    fn is_na(&self) -> bool;
27    fn na() -> Self;
28}
29
30/// ```
31/// use extendr_api::prelude::*;
32/// test! {
33///     assert!(f64::na().is_na());
34/// }
35/// ```
36impl CanBeNA for f64 {
37    fn is_na(&self) -> bool {
38        unsafe { R_IsNA(*self) != 0 }
39    }
40
41    fn na() -> f64 {
42        unsafe { R_NaReal }
43    }
44}
45
46/// ```
47/// use extendr_api::prelude::*;
48/// test! {
49///     assert!(i32::na().is_na());
50/// }
51/// ```
52impl CanBeNA for i32 {
53    fn is_na(&self) -> bool {
54        *self == i32::na()
55    }
56
57    fn na() -> i32 {
58        i32::MIN
59    }
60}
61
62/// Special "NA" string that represents null strings.
63/// ```
64/// use extendr_api::prelude::*;
65/// test! {
66///     assert_ne!(<&str>::na().as_ptr(), "NA".as_ptr());
67///     assert_eq!(<&str>::na(), "NA");
68///     assert_eq!("NA".is_na(), false);
69///     assert_eq!(<&str>::na().is_na(), true);
70/// }
71/// ```
72impl CanBeNA for &str {
73    /// Check for NA in a string by address.
74    fn is_na(&self) -> bool {
75        self.as_ptr() == <&str>::na().as_ptr()
76    }
77
78    fn na() -> Self {
79        &EXTENDR_NA_STRING
80    }
81}