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`.
9495use crate::SEXP;
96use crate::from_r::{SexpError, TryFromSexp};
9798/// 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.
126Present(T),
127/// The argument was not provided (missing in R).
128Absent,
129}
130131impl<T> Missing<T> {
132/// Returns `true` if the argument was provided.
133#[inline]
134pub fn is_present(&self) -> bool {
135#[allow(non_exhaustive_omitted_patterns)] match self {
Missing::Present(_) => true,
_ => false,
}matches!(self, Missing::Present(_))136 }
137138/// Returns `true` if the argument was not provided.
139 ///
140 /// Named to match R's `missing()` function.
141#[inline]
142pub fn is_missing(&self) -> bool {
143#[allow(non_exhaustive_omitted_patterns)] match self {
Missing::Absent => true,
_ => false,
}matches!(self, Missing::Absent)144 }
145146/// Convert to `Option<T>`, returning `None` if missing.
147#[inline]
148pub fn into_option(self) -> Option<T> {
149match self {
150 Missing::Present(v) => Some(v),
151 Missing::Absent => None,
152 }
153 }
154155/// Get a reference to the value if present.
156#[inline]
157pub fn as_ref(&self) -> Missing<&T> {
158match self {
159 Missing::Present(v) => Missing::Present(v),
160 Missing::Absent => Missing::Absent,
161 }
162 }
163164/// Get a mutable reference to the value if present.
165#[inline]
166pub fn as_mut(&mut self) -> Missing<&mut T> {
167match self {
168 Missing::Present(v) => Missing::Present(v),
169 Missing::Absent => Missing::Absent,
170 }
171 }
172173/// Returns the contained value or a default.
174#[inline]
175pub fn unwrap_or(self, default: T) -> T {
176match self {
177 Missing::Present(v) => v,
178 Missing::Absent => default,
179 }
180 }
181182/// Returns the contained value or computes it from a closure.
183#[inline]
184pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
185match self {
186 Missing::Present(v) => v,
187 Missing::Absent => f(),
188 }
189 }
190191/// Maps `Missing<T>` to `Missing<U>` by applying a function.
192#[inline]
193pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Missing<U> {
194match self {
195 Missing::Present(v) => Missing::Present(f(v)),
196 Missing::Absent => Missing::Absent,
197 }
198 }
199200/// Returns the contained value, panicking if absent.
201 ///
202 /// # Panics
203 ///
204 /// Panics if the value is `Absent`.
205#[inline]
206pub fn unwrap(self) -> T {
207match 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 }
212213/// 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]
219pub fn expect(self, msg: &str) -> T {
220match self {
221 Missing::Present(v) => v,
222 Missing::Absent => { ::core::panicking::panic_display(&msg); }panic!("{}", msg),
223 }
224 }
225}
226227impl<T: Default> Missing<T> {
228/// Returns the contained value or the default for that type.
229#[inline]
230pub fn unwrap_or_default(self) -> T {
231match self {
232 Missing::Present(v) => v,
233 Missing::Absent => T::default(),
234 }
235 }
236}
237238impl<T> Defaultfor Missing<T> {
239/// The default is `Absent`.
240#[inline]
241fn default() -> Self {
242 Missing::Absent243 }
244}
245246impl<T> From<T> for Missing<T> {
247#[inline]
248fn from(value: T) -> Self {
249 Missing::Present(value)
250 }
251}
252253impl<T> From<Option<T>> for Missing<T> {
254#[inline]
255fn from(opt: Option<T>) -> Self {
256match opt {
257Some(v) => Missing::Present(v),
258None => Missing::Absent,
259 }
260 }
261}
262263impl<T> From<Missing<T>> for Option<T> {
264#[inline]
265fn from(missing: Missing<T>) -> Self {
266missing.into_option()
267 }
268}
269270// region: TryFromSexp implementation
271272/// 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}
277278impl<T> TryFromSexpfor Missing<T>
279where
280T: TryFromSexp,
281 <T as TryFromSexp>::Error: Into<SexpError>,
282{
283const NATIVE_BORROW: Option<crate::from_r::NativeBorrow> = T::NATIVE_BORROW;
284const CHARACTER_ONLY: bool = T::CHARACTER_ONLY;
285286type Error = SexpError;
287// An omitted argument passes `no_na`.
288#[inline]
289fn __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 }
292293// A given argument is read by its value's type (an `Either` asks the arm
294 // it converted to).
295#[inline]
296fn __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 }
299300fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
301if is_missing_arg(sexp) {
302Ok(Missing::Absent)
303 } else {
304 T::try_from_sexp(sexp)
305 .map(Missing::Present)
306 .map_err(Into::into)
307 }
308 }
309310unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
311if is_missing_arg(sexp) {
312Ok(Missing::Absent)
313 } else {
314unsafe { T::try_from_sexp_unchecked(sexp) }
315 .map(Missing::Present)
316 .map_err(Into::into)
317 }
318 }
319}
320321#[cfg(test)]
322mod tests {
323use super::*;
324325#[test]
326fn missing_methods() {
327let present: Missing<i32> = Missing::Present(42);
328let absent: Missing<i32> = Missing::Absent;
329330assert!(present.is_present());
331assert!(!present.is_missing());
332333assert!(!absent.is_present());
334assert!(absent.is_missing());
335336assert_eq!(present.into_option(), Some(42));
337assert_eq!(absent.into_option(), None);
338 }
339340#[test]
341fn missing_unwrap_or() {
342let present: Missing<i32> = Missing::Present(42);
343let absent: Missing<i32> = Missing::Absent;
344345assert_eq!(present.unwrap_or(0), 42);
346assert_eq!(absent.unwrap_or(0), 0);
347 }
348349#[test]
350fn missing_map() {
351let present: Missing<i32> = Missing::Present(21);
352let absent: Missing<i32> = Missing::Absent;
353354assert_eq!(present.map(|x| x * 2), Missing::Present(42));
355assert_eq!(absent.map(|x| x * 2), Missing::Absent);
356 }
357358#[test]
359fn missing_default() {
360let m: Missing<i32> = Missing::default();
361assert!(m.is_missing());
362 }
363364#[test]
365fn missing_from_option() {
366let some: Missing<i32> = Some(42).into();
367let none: Missing<i32> = None.into();
368369assert_eq!(some, Missing::Present(42));
370assert_eq!(none, Missing::Absent);
371 }
372373/// `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]
377fn missing_list_converts() {
378fn is_param<T: TryFromSexp>() {}
379 is_param::<Missing<crate::list::List>>();
380381let type_err: <crate::list::List as TryFromSexp>::Error = crate::from_r::SexpTypeError {
382 expected: crate::SEXPTYPE::VECSXP,
383 actual: crate::SEXPTYPE::STRSXP,
384 };
385assert!(matches!(SexpError::from(type_err), SexpError::Type(_)));
386 }
387}
388// endregion