Skip to main content

miniextendr_api/
missing.rs

1//! Support for R's missing arguments.
2//!
3//! When an R function is called without providing a value for a formal argument,
4//! R passes `SEXP::missing_arg()` as a placeholder. This is different from `NULL` -
5//! a missing argument means "not provided", while `NULL` is an explicit value.
6//!
7//! # Example
8//!
9//! ```ignore
10//! use miniextendr_api::{miniextendr, Missing};
11//!
12//! #[miniextendr]
13//! fn greet(name: Missing<String>) -> String {
14//!     match name.into_option() {
15//!         Some(n) => format!("Hello, {}!", n),
16//!         None => "Hello, stranger!".to_string(),
17//!     }
18//! }
19//! ```
20//!
21//! In R:
22//! ```r
23//! greet("Alice")  # "Hello, Alice!"
24//! greet()         # "Hello, stranger!"
25//! ```
26//!
27//! # Difference from `Option<T>`
28//!
29//! - `Option<T>` treats `NULL` as `None` and any other value as `Some(T)`.
30//! - `Missing<T>` treats `SEXP::missing_arg()` as missing and any other value (including `NULL`) as present.
31//!
32//! Use `Missing<Option<T>>` if you need to distinguish between:
33//! - Missing argument (not passed)
34//! - `NULL` (explicitly passed `NULL`)
35//! - A value (explicitly passed a non-NULL value)
36//!
37//! # How it works: the `quote(expr=)` sentinel
38//!
39//! In regular R functions, you detect missing arguments with `missing()`:
40//!
41//! ```r
42//! my_func <- function(x) {
43//!   if (missing(x)) "not provided" else x
44//! }
45//! my_func()   # "not provided"
46//! my_func(5)  # 5
47//! ```
48//!
49//! However, `.Call()` (the FFI boundary) forces argument evaluation, so an
50//! omitted argument errors before the call even reaches Rust:
51//!
52//! ```r
53//! wrapper <- function(x) .Call(rust_fn, x)
54//! wrapper()  # Error: argument "x" is missing, with no default
55//! ```
56//!
57//! The generated R wrapper tests `missing()` at the argument position of the
58//! `.Call()`:
59//!
60//! ```r
61//! wrapper <- function(x) .Call(rust_fn, if (missing(x)) quote(expr=) else x)
62//! ```
63//!
64//! `quote(expr=)` is an R idiom for the "missing" state: calling `quote()`
65//! without its `expr` argument returns `SEXP::missing_arg()`. The call
66//! proceeds, the sentinel passes through `.Call()`, and `Missing<T>` reads it
67//! as `Missing::Absent`. Only the argument position may hold the sentinel: a
68//! variable bound to it errors on lookup.
69//!
70//! # A default on `Missing<T>`
71//!
72//! `#[miniextendr(default = "...")]` on a `Missing<T>` parameter (or
73//! `defaults(x = "...")` on a method) only writes the formal, so the usage
74//! line shows the value the function stands for when the argument is left
75//! out:
76//!
77//! ```r
78//! wrapper <- function(tol = 1e-8) .Call(rust_fn, if (missing(tol)) quote(expr=) else tol)
79//! ```
80//!
81//! `missing(tol)` is `TRUE` for an omitted argument even when the formal has
82//! a default, so an omitted argument still reaches Rust as `Missing::Absent`
83//! and the default is never evaluated. The Rust code decides what absence
84//! means; the default in the formal documents it.
85//!
86//! A supplied value is always `Missing::Present`, even one `identical()` to
87//! the default: R's `missing(x)` is `FALSE` once the caller supplies `x`, and
88//! a caller who writes `wrapper(tol = 1e-8)` may mean that value. A caller
89//! forwarding its own formal, `function(tol = 1e-8) wrapper(tol)`, hands its
90//! default over as a value too. This differs from a single-choice
91//! `match_arg` / `choices` parameter under `Missing<..>`, whose formal is the
92//! whole choice vector: that vector is never one valid choice, so passing it
93//! whole reads as omitted. Such a choice parameter takes no `default`.
94
95use crate::SEXP;
96use crate::from_r::{SexpError, TryFromSexp};
97
98/// Wrapper type that detects if an R argument was not passed (missing).
99///
100/// This corresponds to R's `missing()` function. When a function parameter
101/// has type `Missing<T>`, it will be `Missing::Absent` if the caller didn't
102/// provide that argument, or `Missing::Present(value)` if they did.
103///
104/// # Example
105///
106/// ```ignore
107/// use miniextendr_api::{miniextendr, Missing};
108///
109/// #[miniextendr]
110/// fn maybe_square(x: Missing<f64>) -> f64 {
111///     match x {
112///         Missing::Present(val) => val * val,
113///         Missing::Absent => 0.0,
114///     }
115/// }
116/// ```
117///
118/// In R:
119/// ```r
120/// maybe_square(5)  # 25
121/// maybe_square()   # 0
122/// ```
123#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for Missing<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Present(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Present", &__self_0),
            Self::Absent => ::core::fmt::Formatter::write_str(f, "Absent"),
        }
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for Missing<T> {
    #[inline]
    fn clone(&self) -> Self {
        match self {
            Self::Present(__self_0) =>
                Missing::Present(::core::clone::Clone::clone(__self_0)),
            Self::Absent => Missing::Absent,
        }
    }
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for Missing<T> { }Copy, #[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::marker::StructuralPartialEq for
    Missing<T> {
}
#[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for Missing<T> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::Present(__self_0), Self::Present(__arg1_0)) =>
                    __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl<T: ::core::cmp::Eq> ::core::cmp::Eq for Missing<T> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) { let _: ::core::cmp::AssertParamIsEq<T>; }
}Eq)]
124pub enum Missing<T> {
125    /// The argument was provided.
126    Present(T),
127    /// The argument was not provided (missing in R).
128    Absent,
129}
130
131impl<T> Missing<T> {
132    /// Returns `true` if the argument was provided.
133    #[inline]
134    pub fn is_present(&self) -> bool {
135        #[allow(non_exhaustive_omitted_patterns)] match self {
    Missing::Present(_) => true,
    _ => false,
}matches!(self, Missing::Present(_))
136    }
137
138    /// Returns `true` if the argument was not provided.
139    ///
140    /// Named to match R's `missing()` function.
141    #[inline]
142    pub fn is_missing(&self) -> bool {
143        #[allow(non_exhaustive_omitted_patterns)] match self {
    Missing::Absent => true,
    _ => false,
}matches!(self, Missing::Absent)
144    }
145
146    /// Convert to `Option<T>`, returning `None` if missing.
147    #[inline]
148    pub fn into_option(self) -> Option<T> {
149        match self {
150            Missing::Present(v) => Some(v),
151            Missing::Absent => None,
152        }
153    }
154
155    /// Get a reference to the value if present.
156    #[inline]
157    pub fn as_ref(&self) -> Missing<&T> {
158        match self {
159            Missing::Present(v) => Missing::Present(v),
160            Missing::Absent => Missing::Absent,
161        }
162    }
163
164    /// Get a mutable reference to the value if present.
165    #[inline]
166    pub fn as_mut(&mut self) -> Missing<&mut T> {
167        match self {
168            Missing::Present(v) => Missing::Present(v),
169            Missing::Absent => Missing::Absent,
170        }
171    }
172
173    /// Returns the contained value or a default.
174    #[inline]
175    pub fn unwrap_or(self, default: T) -> T {
176        match self {
177            Missing::Present(v) => v,
178            Missing::Absent => default,
179        }
180    }
181
182    /// Returns the contained value or computes it from a closure.
183    #[inline]
184    pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
185        match self {
186            Missing::Present(v) => v,
187            Missing::Absent => f(),
188        }
189    }
190
191    /// Maps `Missing<T>` to `Missing<U>` by applying a function.
192    #[inline]
193    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Missing<U> {
194        match self {
195            Missing::Present(v) => Missing::Present(f(v)),
196            Missing::Absent => Missing::Absent,
197        }
198    }
199
200    /// Returns the contained value, panicking if absent.
201    ///
202    /// # Panics
203    ///
204    /// Panics if the value is `Absent`.
205    #[inline]
206    pub fn unwrap(self) -> T {
207        match self {
208            Missing::Present(v) => v,
209            Missing::Absent => {
    ::core::panicking::panic_fmt(format_args!("called `Missing::unwrap()` on an `Absent` value"));
}panic!("called `Missing::unwrap()` on an `Absent` value"),
210        }
211    }
212
213    /// Returns the contained value, panicking with a custom message if absent.
214    ///
215    /// # Panics
216    ///
217    /// Panics with the provided message if the value is `Absent`.
218    #[inline]
219    pub fn expect(self, msg: &str) -> T {
220        match self {
221            Missing::Present(v) => v,
222            Missing::Absent => { ::core::panicking::panic_display(&msg); }panic!("{}", msg),
223        }
224    }
225}
226
227impl<T: Default> Missing<T> {
228    /// Returns the contained value or the default for that type.
229    #[inline]
230    pub fn unwrap_or_default(self) -> T {
231        match self {
232            Missing::Present(v) => v,
233            Missing::Absent => T::default(),
234        }
235    }
236}
237
238impl<T> Default for Missing<T> {
239    /// The default is `Absent`.
240    #[inline]
241    fn default() -> Self {
242        Missing::Absent
243    }
244}
245
246impl<T> From<T> for Missing<T> {
247    #[inline]
248    fn from(value: T) -> Self {
249        Missing::Present(value)
250    }
251}
252
253impl<T> From<Option<T>> for Missing<T> {
254    #[inline]
255    fn from(opt: Option<T>) -> Self {
256        match opt {
257            Some(v) => Missing::Present(v),
258            None => Missing::Absent,
259        }
260    }
261}
262
263impl<T> From<Missing<T>> for Option<T> {
264    #[inline]
265    fn from(missing: Missing<T>) -> Self {
266        missing.into_option()
267    }
268}
269
270// region: TryFromSexp implementation
271
272/// Check if a SEXP is the missing argument sentinel.
273#[inline]
274pub fn is_missing_arg(sexp: SEXP) -> bool {
275    std::ptr::addr_eq(sexp.0, SEXP::missing_arg().0)
276}
277
278impl<T> TryFromSexp for Missing<T>
279where
280    T: TryFromSexp,
281    <T as TryFromSexp>::Error: Into<SexpError>,
282{
283    const NATIVE_BORROW: Option<crate::from_r::NativeBorrow> = T::NATIVE_BORROW;
284    const CHARACTER_ONLY: bool = T::CHARACTER_ONLY;
285
286    type Error = SexpError;
287    // An omitted argument passes `no_na`.
288    #[inline]
289    fn __mx_has_na(&self) -> bool {
290        #[allow(non_exhaustive_omitted_patterns)] match self {
    Missing::Present(v) if v.__mx_has_na() => true,
    _ => false,
}matches!(self, Missing::Present(v) if v.__mx_has_na())
291    }
292
293    // A given argument is read by its value's type (an `Either` asks the arm
294    // it converted to).
295    #[inline]
296    fn __mx_input_has_na(&self, input: SEXP) -> bool {
297        #[allow(non_exhaustive_omitted_patterns)] match self {
    Missing::Present(v) if v.__mx_input_has_na(input) => true,
    _ => false,
}matches!(self, Missing::Present(v) if v.__mx_input_has_na(input))
298    }
299
300    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
301        if is_missing_arg(sexp) {
302            Ok(Missing::Absent)
303        } else {
304            T::try_from_sexp(sexp)
305                .map(Missing::Present)
306                .map_err(Into::into)
307        }
308    }
309
310    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
311        if is_missing_arg(sexp) {
312            Ok(Missing::Absent)
313        } else {
314            unsafe { T::try_from_sexp_unchecked(sexp) }
315                .map(Missing::Present)
316                .map_err(Into::into)
317        }
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324
325    #[test]
326    fn missing_methods() {
327        let present: Missing<i32> = Missing::Present(42);
328        let absent: Missing<i32> = Missing::Absent;
329
330        assert!(present.is_present());
331        assert!(!present.is_missing());
332
333        assert!(!absent.is_present());
334        assert!(absent.is_missing());
335
336        assert_eq!(present.into_option(), Some(42));
337        assert_eq!(absent.into_option(), None);
338    }
339
340    #[test]
341    fn missing_unwrap_or() {
342        let present: Missing<i32> = Missing::Present(42);
343        let absent: Missing<i32> = Missing::Absent;
344
345        assert_eq!(present.unwrap_or(0), 42);
346        assert_eq!(absent.unwrap_or(0), 0);
347    }
348
349    #[test]
350    fn missing_map() {
351        let present: Missing<i32> = Missing::Present(21);
352        let absent: Missing<i32> = Missing::Absent;
353
354        assert_eq!(present.map(|x| x * 2), Missing::Present(42));
355        assert_eq!(absent.map(|x| x * 2), Missing::Absent);
356    }
357
358    #[test]
359    fn missing_default() {
360        let m: Missing<i32> = Missing::default();
361        assert!(m.is_missing());
362    }
363
364    #[test]
365    fn missing_from_option() {
366        let some: Missing<i32> = Some(42).into();
367        let none: Missing<i32> = None.into();
368
369        assert_eq!(some, Missing::Present(42));
370        assert_eq!(none, Missing::Absent);
371    }
372
373    /// `Missing<List>` is a parameter type: `List`'s conversion error (a
374    /// type error, the only way a `List` conversion fails) reaches
375    /// `SexpError::Type`.
376    #[test]
377    fn missing_list_converts() {
378        fn is_param<T: TryFromSexp>() {}
379        is_param::<Missing<crate::list::List>>();
380
381        let type_err: <crate::list::List as TryFromSexp>::Error = crate::from_r::SexpTypeError {
382            expected: crate::SEXPTYPE::VECSXP,
383            actual: crate::SEXPTYPE::STRSXP,
384        };
385        assert!(matches!(SexpError::from(type_err), SexpError::Type(_)));
386    }
387}
388// endregion