Skip to main content

miniextendr_api/into_r/
large_integers.rs

1//! Large integer types → REALSXP (f64).
2//!
3//! R doesn't have native 64-bit integers. These types convert to f64 (REALSXP)
4//! which may lose precision for values outside the "safe integer" range.
5
6use crate::altrep_traits::{NA_INTEGER, NA_LOGICAL, NA_REAL};
7use crate::into_r::IntoR;
8//
9// **Precision Loss Warning:**
10// - f64 can exactly represent integers in range [-2^53, 2^53] (±9,007,199,254,740,992)
11// - Values outside this range may be rounded to the nearest representable f64
12// - This is silent - no error or warning is raised
13//
14// **Alternatives for exact 64-bit integers:**
15// - Use the `bit64` R package (stores as REALSXP but interprets as int64)
16// - Store as character strings and parse in R
17// - Split into high/low 32-bit parts
18//
19// For most use cases (counters, IDs, timestamps), values fit within 2^53.
20
21/// Convert `i64` to R integer (INTSXP) or numeric (REALSXP).
22///
23/// Uses smart conversion: values in `(i32::MIN, i32::MAX]` are returned as
24/// R integers for exact representation. Values outside that range (including
25/// `i32::MIN` which is `NA_integer_` in R) fall back to R doubles.
26///
27/// ```ignore
28/// let small: i64 = 42;
29/// small.into_sexp(); // R integer 42L
30///
31/// let big: i64 = 3_000_000_000;
32/// big.into_sexp(); // R double 3e9
33///
34/// let na_trap: i64 = i32::MIN as i64;
35/// na_trap.into_sexp(); // R double (not NA_integer_!)
36/// ```
37impl IntoR for i64 {
38    type Error = std::convert::Infallible;
39    #[inline]
40    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
41        Ok(self.into_sexp())
42    }
43    #[inline]
44    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
45        Ok(unsafe { self.into_sexp_unchecked() })
46    }
47    #[inline]
48    fn into_sexp(self) -> crate::ffi::SEXP {
49        // i32::MIN is NA_integer_ in R, so exclude it from the integer range
50        if self > i32::MIN as i64 && self <= i32::MAX as i64 {
51            // Range guard verified — cast is safe
52            (self as i32).into_sexp()
53        } else {
54            // R has no 64-bit integer; f64 loses precision > 2^53
55            (self as f64).into_sexp()
56        }
57    }
58    #[inline]
59    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
60        if self > i32::MIN as i64 && self <= i32::MAX as i64 {
61            unsafe { (self as i32).into_sexp_unchecked() }
62        } else {
63            unsafe { (self as f64).into_sexp_unchecked() }
64        }
65    }
66}
67
68/// Convert `u64` to R integer (INTSXP) or numeric (REALSXP).
69///
70/// Values in `[0, i32::MAX]` are returned as R integers. Larger values
71/// fall back to R doubles (which may lose precision above 2^53).
72impl IntoR for u64 {
73    type Error = std::convert::Infallible;
74    #[inline]
75    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
76        Ok(self.into_sexp())
77    }
78    #[inline]
79    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
80        Ok(unsafe { self.into_sexp_unchecked() })
81    }
82    #[inline]
83    fn into_sexp(self) -> crate::ffi::SEXP {
84        if self <= i32::MAX as u64 {
85            (self as i32).into_sexp()
86        } else {
87            (self as f64).into_sexp()
88        }
89    }
90    #[inline]
91    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
92        if self <= i32::MAX as u64 {
93            unsafe { (self as i32).into_sexp_unchecked() }
94        } else {
95            unsafe { (self as f64).into_sexp_unchecked() }
96        }
97    }
98}
99
100/// Convert `isize` to R integer (INTSXP) or numeric (REALSXP).
101///
102/// On 64-bit platforms, uses the same smart conversion as [`i64`](impl IntoR for i64).
103/// On 32-bit platforms, `isize` fits in i32 so conversion is always exact.
104impl IntoR for isize {
105    type Error = std::convert::Infallible;
106    #[inline]
107    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
108        Ok((self as i64).into_sexp())
109    }
110    #[inline]
111    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
112        Ok(unsafe { self.into_sexp_unchecked() })
113    }
114    #[inline]
115    fn into_sexp(self) -> crate::ffi::SEXP {
116        (self as i64).into_sexp()
117    }
118    #[inline]
119    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
120        unsafe { (self as i64).into_sexp_unchecked() }
121    }
122}
123
124/// Convert `usize` to R integer (INTSXP) or numeric (REALSXP).
125///
126/// Values in `[0, i32::MAX]` are returned as R integers. Larger values
127/// fall back to R doubles.
128impl IntoR for usize {
129    type Error = std::convert::Infallible;
130    #[inline]
131    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
132        Ok((self as u64).into_sexp())
133    }
134    #[inline]
135    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
136        Ok(unsafe { self.into_sexp_unchecked() })
137    }
138    #[inline]
139    fn into_sexp(self) -> crate::ffi::SEXP {
140        (self as u64).into_sexp()
141    }
142    #[inline]
143    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
144        unsafe { (self as u64).into_sexp_unchecked() }
145    }
146}
147
148/// Macro for logical IntoR via Rf_ScalarLogical with conversion to i32.
149macro_rules! impl_logical_into_r {
150    ($ty:ty, $to_i32:expr) => {
151        impl IntoR for $ty {
152            type Error = std::convert::Infallible;
153            #[inline]
154            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
155                Ok(crate::ffi::SEXP::scalar_logical_raw($to_i32(self)))
156            }
157            #[inline]
158            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
159                Ok(unsafe { self.into_sexp_unchecked() })
160            }
161            #[inline]
162            fn into_sexp(self) -> crate::ffi::SEXP {
163                crate::ffi::SEXP::scalar_logical_raw($to_i32(self))
164            }
165            #[inline]
166            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
167                unsafe { crate::ffi::Rf_ScalarLogical_unchecked($to_i32(self)) }
168            }
169        }
170    };
171}
172
173impl_logical_into_r!(bool, |v: bool| i32::from(v));
174impl_logical_into_r!(crate::ffi::Rboolean, |v: crate::ffi::Rboolean| v as i32);
175impl_logical_into_r!(crate::ffi::RLogical, crate::ffi::RLogical::to_i32);
176
177impl IntoR for Option<i32> {
178    type Error = std::convert::Infallible;
179    #[inline]
180    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
181        Ok(self.into_sexp())
182    }
183    #[inline]
184    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
185        Ok(unsafe { self.into_sexp_unchecked() })
186    }
187    #[inline]
188    fn into_sexp(self) -> crate::ffi::SEXP {
189        match self {
190            Some(v) => v.into_sexp(),
191            None => crate::ffi::SEXP::scalar_integer(NA_INTEGER),
192        }
193    }
194    #[inline]
195    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
196        match self {
197            Some(v) => unsafe { v.into_sexp_unchecked() },
198            None => unsafe { crate::ffi::Rf_ScalarInteger_unchecked(NA_INTEGER) },
199        }
200    }
201}
202
203impl IntoR for Option<f64> {
204    type Error = std::convert::Infallible;
205    #[inline]
206    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
207        Ok(self.into_sexp())
208    }
209    #[inline]
210    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
211        Ok(unsafe { self.into_sexp_unchecked() })
212    }
213    #[inline]
214    fn into_sexp(self) -> crate::ffi::SEXP {
215        match self {
216            Some(v) => v.into_sexp(),
217            None => crate::ffi::SEXP::scalar_real(NA_REAL),
218        }
219    }
220    #[inline]
221    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
222        match self {
223            Some(v) => unsafe { v.into_sexp_unchecked() },
224            None => unsafe { crate::ffi::Rf_ScalarReal_unchecked(NA_REAL) },
225        }
226    }
227}
228
229impl IntoR for Option<crate::ffi::Rboolean> {
230    type Error = std::convert::Infallible;
231    #[inline]
232    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
233        Ok(self.into_sexp())
234    }
235    #[inline]
236    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
237        Ok(unsafe { self.into_sexp_unchecked() })
238    }
239    #[inline]
240    fn into_sexp(self) -> crate::ffi::SEXP {
241        match self {
242            // Rboolean is repr(i32), `as i32` is a no-op transmute
243            Some(v) => crate::ffi::SEXP::scalar_logical_raw(v as i32),
244            None => crate::ffi::SEXP::scalar_logical_raw(NA_LOGICAL),
245        }
246    }
247    #[inline]
248    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
249        match self {
250            Some(v) => unsafe { crate::ffi::Rf_ScalarLogical_unchecked(v as i32) },
251            None => unsafe { crate::ffi::Rf_ScalarLogical_unchecked(NA_LOGICAL) },
252        }
253    }
254}
255
256impl IntoR for Option<crate::ffi::RLogical> {
257    type Error = std::convert::Infallible;
258    #[inline]
259    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
260        Ok(self.into_sexp())
261    }
262    #[inline]
263    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
264        Ok(unsafe { self.into_sexp_unchecked() })
265    }
266    #[inline]
267    fn into_sexp(self) -> crate::ffi::SEXP {
268        match self {
269            Some(v) => crate::ffi::SEXP::scalar_logical_raw(v.to_i32()),
270            None => crate::ffi::SEXP::scalar_logical_raw(NA_LOGICAL),
271        }
272    }
273    #[inline]
274    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
275        match self {
276            Some(v) => unsafe { crate::ffi::Rf_ScalarLogical_unchecked(v.to_i32()) },
277            None => unsafe { crate::ffi::Rf_ScalarLogical_unchecked(NA_LOGICAL) },
278        }
279    }
280}
281
282impl IntoR for Option<bool> {
283    type Error = std::convert::Infallible;
284    #[inline]
285    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
286        Ok(self.into_sexp())
287    }
288    #[inline]
289    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
290        Ok(unsafe { self.into_sexp_unchecked() })
291    }
292    #[inline]
293    fn into_sexp(self) -> crate::ffi::SEXP {
294        match self {
295            Some(v) => v.into_sexp(),
296            None => crate::ffi::SEXP::scalar_logical_raw(NA_LOGICAL),
297        }
298    }
299    #[inline]
300    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
301        match self {
302            Some(v) => unsafe { v.into_sexp_unchecked() },
303            None => unsafe { crate::ffi::Rf_ScalarLogical_unchecked(NA_LOGICAL) },
304        }
305    }
306}
307
308/// Macro for NA-aware `Option<T> → R` smart scalar conversion.
309/// Checks if value fits i32 → INTSXP with NA_INTEGER for None,
310/// otherwise REALSXP with NA_REAL for None.
311macro_rules! impl_option_smart_i64_into_r {
312    ($t:ty, $fits_i32:expr) => {
313        impl IntoR for Option<$t> {
314            type Error = std::convert::Infallible;
315            #[inline]
316            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
317                Ok(self.into_sexp())
318            }
319            #[inline]
320            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
321                self.try_into_sexp()
322            }
323            #[inline]
324            fn into_sexp(self) -> crate::ffi::SEXP {
325                match self {
326                    Some(x) if $fits_i32(x) => (x as i32).into_sexp(),
327                    Some(x) => (x as f64).into_sexp(),
328                    None => crate::ffi::SEXP::scalar_integer(NA_INTEGER),
329                }
330            }
331        }
332    };
333}
334
335impl_option_smart_i64_into_r!(i64, |x: i64| x > i32::MIN as i64 && x <= i32::MAX as i64);
336impl_option_smart_i64_into_r!(u64, |x: u64| x <= i32::MAX as u64);
337impl_option_smart_i64_into_r!(isize, |x: isize| x > i32::MIN as isize
338    && x <= i32::MAX as isize);
339impl_option_smart_i64_into_r!(usize, |x: usize| x <= i32::MAX as usize);
340
341/// Macro for `Option<T>` where `T` coerces to a type with existing Option impl.
342macro_rules! impl_option_coerce_into_r {
343    ($from:ty => $to:ty) => {
344        impl IntoR for Option<$from> {
345            type Error = std::convert::Infallible;
346            #[inline]
347            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
348                Ok(self.map(|x| x as $to).into_sexp())
349            }
350            #[inline]
351            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
352                self.try_into_sexp()
353            }
354            #[inline]
355            fn into_sexp(self) -> crate::ffi::SEXP {
356                self.map(|x| x as $to).into_sexp()
357            }
358        }
359    };
360}
361
362impl_option_coerce_into_r!(i8 => i32);
363impl_option_coerce_into_r!(i16 => i32);
364impl_option_coerce_into_r!(u16 => i32);
365impl_option_coerce_into_r!(u32 => i64); // delegates to smart i64 path
366impl_option_coerce_into_r!(f32 => f64);
367
368impl<T: crate::externalptr::TypedExternal> IntoR for crate::externalptr::ExternalPtr<T> {
369    type Error = std::convert::Infallible;
370    #[inline]
371    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
372        Ok(self.as_sexp())
373    }
374    #[inline]
375    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
376        Ok(self.as_sexp())
377    }
378    #[inline]
379    fn into_sexp(self) -> crate::ffi::SEXP {
380        self.as_sexp()
381    }
382}
383
384/// Blanket impl: Types marked with `IntoExternalPtr` get automatic `IntoR`.
385///
386/// This wraps the value in `ExternalPtr<T>` automatically, so you can return
387/// `MyType` directly from `#[miniextendr]` functions instead of `ExternalPtr<MyType>`.
388impl<T: crate::externalptr::IntoExternalPtr> IntoR for T {
389    type Error = std::convert::Infallible;
390    #[inline]
391    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
392        Ok(self.into_sexp())
393    }
394    #[inline]
395    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
396        Ok(unsafe { self.into_sexp_unchecked() })
397    }
398    #[inline]
399    fn into_sexp(self) -> crate::ffi::SEXP {
400        crate::externalptr::ExternalPtr::new(self).into_sexp()
401    }
402    #[inline]
403    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
404        unsafe { crate::externalptr::ExternalPtr::new_unchecked(self).into_sexp() }
405    }
406}
407
408/// Helper to convert a string slice to R CHARSXP.
409/// Uses UTF-8 encoding. Empty strings return R_BlankString (static, no allocation).
410#[inline]
411pub(crate) fn str_to_charsxp(s: &str) -> crate::ffi::SEXP {
412    if s.is_empty() {
413        crate::ffi::SEXP::blank_string()
414    } else {
415        let _len: i32 = s.len().try_into().expect("string exceeds i32::MAX bytes");
416        crate::ffi::SEXP::charsxp(s)
417    }
418}
419
420/// Unchecked version of [`str_to_charsxp`].
421#[inline]
422pub(crate) unsafe fn str_to_charsxp_unchecked(s: &str) -> crate::ffi::SEXP {
423    unsafe {
424        if s.is_empty() {
425            crate::ffi::SEXP::blank_string()
426        } else {
427            let len: i32 = s.len().try_into().expect("string exceeds i32::MAX bytes");
428            crate::ffi::Rf_mkCharLenCE_unchecked(s.as_ptr().cast(), len, crate::ffi::CE_UTF8)
429        }
430    }
431}
432
433impl IntoR for String {
434    type Error = crate::into_r_error::IntoRError;
435    #[inline]
436    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
437        self.as_str().try_into_sexp()
438    }
439    #[inline]
440    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
441        Ok(unsafe { self.into_sexp_unchecked() })
442    }
443    #[inline]
444    fn into_sexp(self) -> crate::ffi::SEXP {
445        self.as_str().into_sexp()
446    }
447    #[inline]
448    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
449        unsafe { self.as_str().into_sexp_unchecked() }
450    }
451}
452
453impl IntoR for char {
454    type Error = std::convert::Infallible;
455    #[inline]
456    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
457        Ok(self.into_sexp())
458    }
459    #[inline]
460    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
461        Ok(unsafe { self.into_sexp_unchecked() })
462    }
463    #[inline]
464    fn into_sexp(self) -> crate::ffi::SEXP {
465        // Convert char to a single-character string — always ≤ 4 bytes, cannot overflow i32
466        let mut buf = [0u8; 4];
467        let s = self.encode_utf8(&mut buf);
468        crate::ffi::SEXP::scalar_string(str_to_charsxp(s))
469    }
470    #[inline]
471    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
472        let mut buf = [0u8; 4];
473        let s = self.encode_utf8(&mut buf);
474        unsafe { s.into_sexp_unchecked() }
475    }
476}
477
478impl IntoR for &str {
479    type Error = crate::into_r_error::IntoRError;
480    #[inline]
481    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
482        let _len = i32::try_from(self.len())
483            .map_err(|_| crate::into_r_error::IntoRError::StringTooLong { len: self.len() })?;
484        Ok(crate::ffi::SEXP::scalar_string(str_to_charsxp(self)))
485    }
486    #[inline]
487    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
488        Ok(unsafe { self.into_sexp_unchecked() })
489    }
490    #[inline]
491    fn into_sexp(self) -> crate::ffi::SEXP {
492        crate::ffi::SEXP::scalar_string(str_to_charsxp(self))
493    }
494    #[inline]
495    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
496        unsafe {
497            let charsxp = str_to_charsxp_unchecked(self);
498            crate::ffi::Rf_ScalarString_unchecked(charsxp)
499        }
500    }
501}
502
503impl IntoR for Option<&str> {
504    type Error = crate::into_r_error::IntoRError;
505    #[inline]
506    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
507        match self {
508            Some(s) => {
509                let _len = i32::try_from(s.len())
510                    .map_err(|_| crate::into_r_error::IntoRError::StringTooLong { len: s.len() })?;
511                Ok(crate::ffi::SEXP::scalar_string(str_to_charsxp(s)))
512            }
513            None => Ok(crate::ffi::SEXP::scalar_string(
514                crate::ffi::SEXP::na_string(),
515            )),
516        }
517    }
518    #[inline]
519    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
520        Ok(unsafe { self.into_sexp_unchecked() })
521    }
522    #[inline]
523    fn into_sexp(self) -> crate::ffi::SEXP {
524        let charsxp = match self {
525            Some(s) => str_to_charsxp(s),
526            None => crate::ffi::SEXP::na_string(),
527        };
528        crate::ffi::SEXP::scalar_string(charsxp)
529    }
530    #[inline]
531    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
532        unsafe {
533            let charsxp = match self {
534                Some(s) => str_to_charsxp_unchecked(s),
535                None => crate::ffi::SEXP::na_string(),
536            };
537            crate::ffi::Rf_ScalarString_unchecked(charsxp)
538        }
539    }
540}
541
542impl IntoR for Option<String> {
543    type Error = crate::into_r_error::IntoRError;
544    #[inline]
545    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
546        self.as_deref().try_into_sexp()
547    }
548    #[inline]
549    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
550        Ok(unsafe { self.into_sexp_unchecked() })
551    }
552    #[inline]
553    fn into_sexp(self) -> crate::ffi::SEXP {
554        self.as_deref().into_sexp()
555    }
556    #[inline]
557    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
558        unsafe { self.as_deref().into_sexp_unchecked() }
559    }
560}
561
562/// Convert `Option<&T>` to R SEXP by copying the value.
563///
564/// - `Some(&v)` → copies `v` and converts to R
565/// - `None` → returns `NULL` (R_NilValue)
566///
567/// Note: This returns NULL for None, not NA, since there's no reference to return.
568/// Use `Option<T>` directly if you want NA semantics for scalar types.
569impl<T> IntoR for Option<&T>
570where
571    T: Copy + IntoR,
572{
573    type Error = crate::into_r_error::IntoRError;
574    #[inline]
575    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
576        match self {
577            Some(&v) => v
578                .try_into_sexp()
579                .map_err(|e| crate::into_r_error::IntoRError::Inner(e.to_string())),
580            None => Ok(crate::ffi::SEXP::nil()),
581        }
582    }
583    #[inline]
584    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
585        Ok(unsafe { self.into_sexp_unchecked() })
586    }
587    #[inline]
588    fn into_sexp(self) -> crate::ffi::SEXP {
589        match self {
590            Some(&v) => v.into_sexp(),
591            None => crate::ffi::SEXP::nil(),
592        }
593    }
594    #[inline]
595    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
596        match self {
597            Some(&v) => unsafe { v.into_sexp_unchecked() },
598            None => crate::ffi::SEXP::nil(),
599        }
600    }
601}
602
603// endregion