Skip to main content

miniextendr_api/
into_r.rs

1#![allow(rustdoc::private_intra_doc_links)]
2//! Conversions from Rust types to R SEXP.
3//!
4//! This module provides the [`IntoR`] trait for converting Rust values to R SEXPs.
5//!
6//! # Submodules
7//!
8//! | Module | Contents |
9//! |--------|----------|
10//! | [`large_integers`] | `i64`, `u64`, `isize`, `usize` → REALSXP, plus string/bool/Option scalars |
11//! | [`collections`] | `HashMap`, `BTreeMap`, `HashSet`, `BTreeSet` → named/unnamed lists |
12//! | [`result`] | `Result<T, E>` → list with `ok`/`err` fields |
13//! | [`altrep`] | `Altrep<T>` marker type, `Lazy<T>` alias, `IntoRAltrep` trait |
14//!
15//! # Thread Safety
16//!
17//! The trait provides two methods:
18//! - [`IntoR::into_sexp`] - checked version with debug thread assertions
19//! - [`IntoR::into_sexp_unchecked`] - unchecked version for performance-critical paths
20//!
21//! Use `into_sexp_unchecked` when you're certain you're on the main thread:
22//! - Inside ALTREP callbacks
23//! - Inside `#[miniextendr(unsafe(main_thread))]` functions
24//! - Inside `extern "C-unwind"` functions called directly by R
25
26use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
27use std::hash::Hash;
28
29use crate::altrep_traits::{NA_INTEGER, NA_LOGICAL, NA_REAL};
30use crate::ffi::SexpExt;
31use crate::gc_protect::OwnedProtect;
32
33/// Trait for converting Rust types to R SEXP values.
34///
35/// # Required Method
36///
37/// Implementors must provide [`try_into_sexp`](IntoR::try_into_sexp) and
38/// specify [`Error`](IntoR::Error). The other three methods have sensible
39/// defaults.
40///
41/// # Examples
42///
43/// ```no_run
44/// use miniextendr_api::into_r::IntoR;
45///
46/// let sexp = 42i32.into_sexp();
47/// let sexp = "hello".to_string().into_sexp();
48///
49/// // Fallible path:
50/// let result = "hello".try_into_sexp();
51/// assert!(result.is_ok());
52/// ```
53pub trait IntoR {
54    /// The error type for fallible conversions.
55    ///
56    /// Use [`std::convert::Infallible`] for types that can never fail.
57    /// Use [`IntoRError`](crate::into_r_error::IntoRError) for types
58    /// that may fail (e.g. strings exceeding R's i32 length limit).
59    type Error: std::fmt::Display;
60
61    /// Try to convert this value to an R SEXP.
62    ///
63    /// This is the **required** method. All other methods delegate to it.
64    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error>;
65
66    /// Try to convert to SEXP without thread safety checks.
67    ///
68    /// # Safety
69    ///
70    /// Must be called from R's main thread.
71    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error>
72    where
73        Self: Sized,
74    {
75        self.try_into_sexp()
76    }
77
78    /// Convert this value to an R SEXP, panicking on error.
79    ///
80    /// In debug builds, asserts that we're on R's main thread.
81    fn into_sexp(self) -> crate::ffi::SEXP
82    where
83        Self: Sized,
84    {
85        match self.try_into_sexp() {
86            Ok(sexp) => sexp,
87            Err(e) => panic!("IntoR conversion failed: {e}"),
88        }
89    }
90
91    /// Convert to SEXP without thread safety checks, panicking on error.
92    ///
93    /// # Safety
94    ///
95    /// Must be called from R's main thread. In debug builds, this still
96    /// calls the checked version by default, but implementations may
97    /// skip thread assertions for performance.
98    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP
99    where
100        Self: Sized,
101    {
102        // Default: just call the checked version
103        self.into_sexp()
104    }
105}
106
107impl IntoR for crate::ffi::SEXP {
108    type Error = std::convert::Infallible;
109    #[inline]
110    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
111        Ok(self)
112    }
113    #[inline]
114    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
115        Ok(self)
116    }
117    #[inline]
118    fn into_sexp(self) -> crate::ffi::SEXP {
119        self
120    }
121}
122
123impl IntoR for crate::worker::Sendable<crate::ffi::SEXP> {
124    type Error = std::convert::Infallible;
125    #[inline]
126    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
127        Ok(self.0)
128    }
129    #[inline]
130    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
131        Ok(self.0)
132    }
133    #[inline]
134    fn into_sexp(self) -> crate::ffi::SEXP {
135        self.0
136    }
137}
138
139impl From<crate::worker::Sendable<crate::ffi::SEXP>> for crate::ffi::SEXP {
140    #[inline]
141    fn from(s: crate::worker::Sendable<crate::ffi::SEXP>) -> Self {
142        s.0
143    }
144}
145
146impl IntoR for () {
147    type Error = std::convert::Infallible;
148    #[inline]
149    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
150        Ok(crate::ffi::SEXP::nil())
151    }
152    #[inline]
153    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
154        self.try_into_sexp()
155    }
156    #[inline]
157    fn into_sexp(self) -> crate::ffi::SEXP {
158        crate::ffi::SEXP::nil()
159    }
160}
161
162impl IntoR for std::convert::Infallible {
163    type Error = std::convert::Infallible;
164    #[inline]
165    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
166        Ok(crate::ffi::SEXP::nil())
167    }
168    #[inline]
169    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
170        self.try_into_sexp()
171    }
172    #[inline]
173    fn into_sexp(self) -> crate::ffi::SEXP {
174        crate::ffi::SEXP::nil()
175    }
176}
177
178/// Macro for scalar IntoR via Rf_Scalar* functions.
179macro_rules! impl_scalar_into_r {
180    ($ty:ty, $checked:ident, $unchecked:ident) => {
181        impl IntoR for $ty {
182            type Error = std::convert::Infallible;
183            #[inline]
184            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
185                Ok(unsafe { crate::ffi::$checked(self) })
186            }
187            #[inline]
188            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
189                Ok(unsafe { self.into_sexp_unchecked() })
190            }
191            #[inline]
192            fn into_sexp(self) -> crate::ffi::SEXP {
193                unsafe { crate::ffi::$checked(self) }
194            }
195            #[inline]
196            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
197                unsafe { crate::ffi::$unchecked(self) }
198            }
199        }
200    };
201}
202
203impl_scalar_into_r!(i32, Rf_ScalarInteger, Rf_ScalarInteger_unchecked);
204impl_scalar_into_r!(f64, Rf_ScalarReal, Rf_ScalarReal_unchecked);
205impl_scalar_into_r!(u8, Rf_ScalarRaw, Rf_ScalarRaw_unchecked);
206impl_scalar_into_r!(
207    crate::ffi::Rcomplex,
208    Rf_ScalarComplex,
209    Rf_ScalarComplex_unchecked
210);
211
212impl IntoR for Option<crate::ffi::Rcomplex> {
213    type Error = std::convert::Infallible;
214    #[inline]
215    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
216        Ok(self.into_sexp())
217    }
218    #[inline]
219    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
220        Ok(unsafe { self.into_sexp_unchecked() })
221    }
222    #[inline]
223    fn into_sexp(self) -> crate::ffi::SEXP {
224        match self {
225            Some(v) => v.into_sexp(),
226            None => crate::ffi::SEXP::scalar_complex(crate::ffi::Rcomplex {
227                r: NA_REAL,
228                i: NA_REAL,
229            }),
230        }
231    }
232    #[inline]
233    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
234        match self {
235            Some(v) => unsafe { v.into_sexp_unchecked() },
236            None => unsafe {
237                crate::ffi::Rf_ScalarComplex_unchecked(crate::ffi::Rcomplex {
238                    r: NA_REAL,
239                    i: NA_REAL,
240                })
241            },
242        }
243    }
244}
245
246/// Macro for infallible widening IntoR via Coerce.
247macro_rules! impl_into_r_via_coerce {
248    ($from:ty => $to:ty) => {
249        impl IntoR for $from {
250            type Error = std::convert::Infallible;
251            #[inline]
252            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
253                Ok(crate::coerce::Coerce::<$to>::coerce(self).into_sexp())
254            }
255            #[inline]
256            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
257                Ok(unsafe { self.into_sexp_unchecked() })
258            }
259            #[inline]
260            fn into_sexp(self) -> crate::ffi::SEXP {
261                crate::coerce::Coerce::<$to>::coerce(self).into_sexp()
262            }
263            #[inline]
264            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
265                unsafe { crate::coerce::Coerce::<$to>::coerce(self).into_sexp_unchecked() }
266            }
267        }
268    };
269}
270
271// Infallible widening to i32 (R's INTSXP)
272impl_into_r_via_coerce!(i8 => i32);
273impl_into_r_via_coerce!(i16 => i32);
274impl_into_r_via_coerce!(u16 => i32);
275
276// Infallible widening to f64 (R's REALSXP)
277impl_into_r_via_coerce!(f32 => f64);
278impl_into_r_via_coerce!(u32 => f64); // all u32 exactly representable in f64
279
280mod large_integers;
281pub(crate) use large_integers::{str_to_charsxp, str_to_charsxp_unchecked};
282
283// region: Vector conversions
284
285impl<T> IntoR for Vec<T>
286where
287    T: crate::ffi::RNativeType,
288{
289    type Error = std::convert::Infallible;
290    #[inline]
291    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
292        Ok(unsafe { vec_to_sexp(&self) })
293    }
294    #[inline]
295    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
296        Ok(unsafe { self.into_sexp_unchecked() })
297    }
298    #[inline]
299    fn into_sexp(self) -> crate::ffi::SEXP {
300        unsafe { vec_to_sexp(&self) }
301    }
302    #[inline]
303    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
304        unsafe { vec_to_sexp_unchecked(&self) }
305    }
306}
307
308impl<T> IntoR for &[T]
309where
310    T: crate::ffi::RNativeType,
311{
312    type Error = std::convert::Infallible;
313    #[inline]
314    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
315        Ok(unsafe { vec_to_sexp(self) })
316    }
317    #[inline]
318    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
319        Ok(unsafe { self.into_sexp_unchecked() })
320    }
321    #[inline]
322    fn into_sexp(self) -> crate::ffi::SEXP {
323        unsafe { vec_to_sexp(self) }
324    }
325    #[inline]
326    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
327        unsafe { vec_to_sexp_unchecked(self) }
328    }
329}
330
331impl<T> IntoR for Box<[T]>
332where
333    T: crate::ffi::RNativeType,
334{
335    type Error = std::convert::Infallible;
336    #[inline]
337    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
338        Ok(unsafe { vec_to_sexp(&self) })
339    }
340    #[inline]
341    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
342        Ok(unsafe { vec_to_sexp_unchecked(&self) })
343    }
344    #[inline]
345    fn into_sexp(self) -> crate::ffi::SEXP {
346        unsafe { vec_to_sexp(&self) }
347    }
348    #[inline]
349    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
350        unsafe { vec_to_sexp_unchecked(&self) }
351    }
352}
353
354// region: R vector allocation helpers
355//
356// These are the ONLY place in the codebase that should call Rf_allocVector
357// for typed vectors and obtain a mutable data slice. All conversion code
358// uses these helpers instead of raw FFI pointer arithmetic.
359
360/// Allocate an R vector of type `T` with `n` elements and return `(SEXP, &mut [T])`.
361///
362/// The returned SEXP is **unprotected** — caller must protect via `Rf_protect`,
363/// `OwnedProtect`, or `ProtectScope` before any further R allocation.
364///
365/// # Safety
366///
367/// Must be called from R's main thread.
368#[inline]
369pub(crate) unsafe fn alloc_r_vector<T: crate::ffi::RNativeType>(
370    n: usize,
371) -> (crate::ffi::SEXP, &'static mut [T]) {
372    unsafe {
373        let sexp = crate::ffi::Rf_allocVector(T::SEXP_TYPE, n as crate::ffi::R_xlen_t);
374        let slice = crate::from_r::r_slice_mut(T::dataptr_mut(sexp), n);
375        (sexp, slice)
376    }
377}
378
379/// Allocate an R vector (unchecked FFI variant).
380///
381/// # Safety
382///
383/// Must be called from R's main thread.
384#[inline]
385pub(crate) unsafe fn alloc_r_vector_unchecked<T: crate::ffi::RNativeType>(
386    n: usize,
387) -> (crate::ffi::SEXP, &'static mut [T]) {
388    unsafe {
389        let sexp = crate::ffi::Rf_allocVector_unchecked(T::SEXP_TYPE, n as crate::ffi::R_xlen_t);
390        let slice = crate::from_r::r_slice_mut(T::dataptr_mut(sexp), n);
391        (sexp, slice)
392    }
393}
394
395// endregion
396
397/// Convert a slice to an R vector (checked) using `copy_from_slice`.
398#[inline]
399unsafe fn vec_to_sexp<T: crate::ffi::RNativeType>(slice: &[T]) -> crate::ffi::SEXP {
400    unsafe {
401        let (sexp, dst) = alloc_r_vector::<T>(slice.len());
402        dst.copy_from_slice(slice);
403        sexp
404    }
405}
406
407/// Convert a slice to an R vector (unchecked) using `copy_from_slice`.
408#[inline]
409unsafe fn vec_to_sexp_unchecked<T: crate::ffi::RNativeType>(slice: &[T]) -> crate::ffi::SEXP {
410    unsafe {
411        let (sexp, dst) = alloc_r_vector_unchecked::<T>(slice.len());
412        dst.copy_from_slice(slice);
413        sexp
414    }
415}
416// endregion
417
418// region: Vec coercion for non-native types (i8, i16, u16 → i32; f32 → f64)
419
420/// Macro for `Vec<T>` where `T` coerces to a native R type.
421///
422/// Allocates the R vector directly and coerces in-place — no intermediate Vec.
423macro_rules! impl_vec_coerce_into_r {
424    ($from:ty => $to:ty) => {
425        impl IntoR for Vec<$from> {
426            type Error = std::convert::Infallible;
427            #[inline]
428            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
429                Ok(self.into_sexp())
430            }
431            #[inline]
432            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
433                Ok(unsafe { self.into_sexp_unchecked() })
434            }
435            #[inline]
436            fn into_sexp(self) -> crate::ffi::SEXP {
437                unsafe {
438                    let (sexp, dst) = alloc_r_vector::<$to>(self.len());
439                    for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
440                        *slot = <$to>::from(val);
441                    }
442                    sexp
443                }
444            }
445            #[inline]
446            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
447                unsafe {
448                    let (sexp, dst) = alloc_r_vector_unchecked::<$to>(self.len());
449                    for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
450                        *slot = <$to>::from(val);
451                    }
452                    sexp
453                }
454            }
455        }
456
457        impl IntoR for &[$from] {
458            type Error = std::convert::Infallible;
459            #[inline]
460            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
461                Ok(self.into_sexp())
462            }
463            #[inline]
464            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
465                Ok(unsafe { self.into_sexp_unchecked() })
466            }
467            #[inline]
468            fn into_sexp(self) -> crate::ffi::SEXP {
469                unsafe {
470                    let (sexp, dst) = alloc_r_vector::<$to>(self.len());
471                    for (slot, &val) in dst.iter_mut().zip(self.iter()) {
472                        *slot = <$to>::from(val);
473                    }
474                    sexp
475                }
476            }
477            #[inline]
478            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
479                unsafe {
480                    let (sexp, dst) = alloc_r_vector_unchecked::<$to>(self.len());
481                    for (slot, &val) in dst.iter_mut().zip(self.iter()) {
482                        *slot = <$to>::from(val);
483                    }
484                    sexp
485                }
486            }
487        }
488    };
489}
490
491// Sub-i32 integer types coerce to i32 (R's INTSXP)
492impl_vec_coerce_into_r!(i8 => i32);
493impl_vec_coerce_into_r!(i16 => i32);
494impl_vec_coerce_into_r!(u16 => i32);
495
496// f32 coerces to f64 (R's REALSXP)
497impl_vec_coerce_into_r!(f32 => f64);
498
499// i64/u64/isize/usize: smart conversion (INTSXP when all fit, else REALSXP)
500//
501// Allocates the R vector directly and coerces in-place — no intermediate Vec.
502macro_rules! impl_vec_smart_i64_into_r {
503    ($t:ty, $fits_i32:expr) => {
504        impl IntoR for Vec<$t> {
505            type Error = std::convert::Infallible;
506            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
507                Ok(self.into_sexp())
508            }
509            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
510                Ok(unsafe { self.into_sexp_unchecked() })
511            }
512            fn into_sexp(self) -> crate::ffi::SEXP {
513                unsafe {
514                    if self.iter().all(|&x| $fits_i32(x)) {
515                        let (sexp, dst) = alloc_r_vector::<i32>(self.len());
516                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
517                            // fits_i32 guard verified range
518                            *slot = val as i32;
519                        }
520                        sexp
521                    } else {
522                        let (sexp, dst) = alloc_r_vector::<f64>(self.len());
523                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
524                            // R has no 64-bit integer; f64 loses precision > 2^53
525                            *slot = val as f64;
526                        }
527                        sexp
528                    }
529                }
530            }
531            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
532                unsafe {
533                    if self.iter().all(|&x| $fits_i32(x)) {
534                        let (sexp, dst) = alloc_r_vector_unchecked::<i32>(self.len());
535                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
536                            // fits_i32 guard verified range
537                            *slot = val as i32;
538                        }
539                        sexp
540                    } else {
541                        let (sexp, dst) = alloc_r_vector_unchecked::<f64>(self.len());
542                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
543                            // R has no 64-bit integer; f64 loses precision > 2^53
544                            *slot = val as f64;
545                        }
546                        sexp
547                    }
548                }
549            }
550        }
551    };
552}
553
554// i32::MIN is NA_integer_ in R, so exclude it
555impl_vec_smart_i64_into_r!(i64, |x: i64| x > i32::MIN as i64 && x <= i32::MAX as i64);
556impl_vec_smart_i64_into_r!(u64, |x: u64| x <= i32::MAX as u64);
557impl_vec_smart_i64_into_r!(isize, |x: isize| x > i32::MIN as isize
558    && x <= i32::MAX as isize);
559impl_vec_smart_i64_into_r!(usize, |x: usize| x <= i32::MAX as usize);
560// endregion
561
562mod altrep;
563mod collections;
564mod result;
565
566pub use altrep::*;
567pub use result::*;
568
569// region: Fixed-size array conversions
570
571/// Blanket impl for `[T; N]` where T: RNativeType.
572///
573/// Enables direct conversion of fixed-size arrays to R vectors.
574/// Useful for SHA hashes, fixed-size byte patterns, etc.
575impl<T: crate::ffi::RNativeType, const N: usize> IntoR for [T; N] {
576    type Error = std::convert::Infallible;
577    #[inline]
578    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
579        Ok(self.as_slice().into_sexp())
580    }
581    #[inline]
582    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
583        Ok(unsafe { self.into_sexp_unchecked() })
584    }
585    #[inline]
586    fn into_sexp(self) -> crate::ffi::SEXP {
587        self.as_slice().into_sexp()
588    }
589    #[inline]
590    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
591        unsafe { self.as_slice().into_sexp_unchecked() }
592    }
593}
594// endregion
595
596// region: VecDeque conversions
597
598use std::collections::VecDeque;
599
600/// Convert `VecDeque<T>` to R vector where T: RNativeType.
601impl<T> IntoR for VecDeque<T>
602where
603    T: crate::ffi::RNativeType,
604{
605    type Error = std::convert::Infallible;
606    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
607        Ok(self.into_sexp())
608    }
609    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
610        Ok(unsafe { self.into_sexp_unchecked() })
611    }
612    fn into_sexp(self) -> crate::ffi::SEXP {
613        let vec: Vec<T> = self.into_iter().collect();
614        vec.into_sexp()
615    }
616    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
617        let vec: Vec<T> = self.into_iter().collect();
618        unsafe { vec.into_sexp_unchecked() }
619    }
620}
621// endregion
622
623// region: BinaryHeap conversions
624
625use std::collections::BinaryHeap;
626
627/// Convert `BinaryHeap<T>` to R vector where T: RNativeType + Ord.
628///
629/// The heap is drained into a vector (destroying the heap property).
630/// Elements are returned in arbitrary order, not sorted.
631impl<T> IntoR for BinaryHeap<T>
632where
633    T: crate::ffi::RNativeType + Ord,
634{
635    type Error = std::convert::Infallible;
636    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
637        Ok(self.into_vec().into_sexp())
638    }
639    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
640        Ok(unsafe { self.into_sexp_unchecked() })
641    }
642    fn into_sexp(self) -> crate::ffi::SEXP {
643        self.into_vec().into_sexp()
644    }
645    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
646        unsafe { self.into_vec().into_sexp_unchecked() }
647    }
648}
649// endregion
650
651// region: Cow conversions
652
653use std::borrow::Cow;
654
655/// Try SEXP pointer recovery for a borrowed Cow slice.
656#[inline]
657#[allow(clippy::ptr_arg)] // Need &Cow to inspect Borrowed vs Owned variant
658fn try_recover_cow_slice<T: crate::ffi::RNativeType>(
659    cow: &Cow<'_, [T]>,
660) -> Option<crate::ffi::SEXP> {
661    if let Cow::Borrowed(slice) = cow {
662        unsafe {
663            crate::r_memory::try_recover_r_sexp(
664                slice.as_ptr() as *const u8,
665                T::SEXP_TYPE,
666                slice.len(),
667            )
668        }
669    } else {
670        None
671    }
672}
673
674/// Convert `Cow<'_, [T]>` to R vector where T: RNativeType.
675///
676/// For `Cow::Borrowed` slices that came from R (e.g., via `TryFromSexp`),
677/// SEXP pointer recovery is attempted — if the borrowed data points into
678/// an R vector, the original SEXP is returned without copying. Otherwise
679/// falls through to the standard copy path.
680impl<T> IntoR for Cow<'_, [T]>
681where
682    T: crate::ffi::RNativeType + Clone,
683{
684    type Error = std::convert::Infallible;
685    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
686        Ok(self.into_sexp())
687    }
688    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
689        Ok(unsafe { self.into_sexp_unchecked() })
690    }
691    fn into_sexp(self) -> crate::ffi::SEXP {
692        if let Some(sexp) = try_recover_cow_slice(&self) {
693            return sexp;
694        }
695        self.as_ref().into_sexp()
696    }
697    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
698        if let Some(sexp) = try_recover_cow_slice(&self) {
699            return sexp;
700        }
701        unsafe { self.as_ref().into_sexp_unchecked() }
702    }
703}
704
705/// Convert `Cow<'_, str>` to R character scalar.
706impl IntoR for Cow<'_, str> {
707    type Error = crate::into_r_error::IntoRError;
708    #[inline]
709    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
710        self.as_ref().try_into_sexp()
711    }
712    #[inline]
713    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
714        Ok(unsafe { self.into_sexp_unchecked() })
715    }
716    #[inline]
717    fn into_sexp(self) -> crate::ffi::SEXP {
718        self.as_ref().into_sexp()
719    }
720    #[inline]
721    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
722        unsafe { self.as_ref().into_sexp_unchecked() }
723    }
724}
725// endregion
726
727// region: Box conversions (skipped - conflicts with IntoExternalPtr blanket impl)
728//
729// We can't add `impl<T: IntoR> IntoR for Box<T>` because it conflicts with
730// the blanket impl `impl<T: IntoExternalPtr> IntoR for T`. If downstream
731// crates implement `IntoExternalPtr for Box<SomeType>`, we'd have overlapping
732// impls. Users can manually unbox with `*boxed_value` before conversion.
733// endregion
734
735// region: PathBuf / OsString conversions
736
737use std::ffi::OsString;
738use std::path::PathBuf;
739
740/// Generate IntoR impls for types with `to_string_lossy()` (owned scalar, ref scalar,
741/// Option, Vec, Vec<Option>). Used for PathBuf/&Path and OsString/&OsStr.
742macro_rules! impl_lossy_string_into_r {
743    (
744        $(#[$owned_meta:meta])*
745        owned: $owned_ty:ty;
746        $(#[$ref_meta:meta])*
747        ref: $ref_ty:ty;
748        $(#[$option_meta:meta])*
749        option: $opt_ty:ty;
750        $(#[$vec_meta:meta])*
751        vec: $vec_ty:ty;
752        $(#[$vec_option_meta:meta])*
753        vec_option: $vec_opt_ty:ty;
754    ) => {
755        $(#[$owned_meta])*
756        impl IntoR for $owned_ty {
757            type Error = crate::into_r_error::IntoRError;
758            #[inline]
759            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
760                self.to_string_lossy().into_owned().try_into_sexp()
761            }
762            #[inline]
763            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
764                Ok(unsafe { self.into_sexp_unchecked() })
765            }
766            #[inline]
767            fn into_sexp(self) -> crate::ffi::SEXP {
768                self.to_string_lossy().into_owned().into_sexp()
769            }
770            #[inline]
771            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
772                unsafe { self.to_string_lossy().into_owned().into_sexp_unchecked() }
773            }
774        }
775
776        $(#[$ref_meta])*
777        impl IntoR for $ref_ty {
778            type Error = crate::into_r_error::IntoRError;
779            #[inline]
780            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
781                self.to_string_lossy().into_owned().try_into_sexp()
782            }
783            #[inline]
784            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
785                Ok(unsafe { self.into_sexp_unchecked() })
786            }
787            #[inline]
788            fn into_sexp(self) -> crate::ffi::SEXP {
789                self.to_string_lossy().into_owned().into_sexp()
790            }
791            #[inline]
792            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
793                unsafe { self.to_string_lossy().into_owned().into_sexp_unchecked() }
794            }
795        }
796
797        $(#[$option_meta])*
798        impl IntoR for Option<$owned_ty> {
799            type Error = crate::into_r_error::IntoRError;
800            #[inline]
801            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
802                self.map(|v| v.to_string_lossy().into_owned()).try_into_sexp()
803            }
804            #[inline]
805            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
806                Ok(unsafe { self.into_sexp_unchecked() })
807            }
808            #[inline]
809            fn into_sexp(self) -> crate::ffi::SEXP {
810                self.map(|v| v.to_string_lossy().into_owned()).into_sexp()
811            }
812            #[inline]
813            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
814                unsafe {
815                    self.map(|v| v.to_string_lossy().into_owned())
816                        .into_sexp_unchecked()
817                }
818            }
819        }
820
821        $(#[$vec_meta])*
822        impl IntoR for Vec<$owned_ty> {
823            type Error = crate::into_r_error::IntoRError;
824            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
825                Ok(self.into_sexp())
826            }
827            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
828                Ok(unsafe { self.into_sexp_unchecked() })
829            }
830            fn into_sexp(self) -> crate::ffi::SEXP {
831                let strings: Vec<String> = self
832                    .into_iter()
833                    .map(|v| v.to_string_lossy().into_owned())
834                    .collect();
835                strings.into_sexp()
836            }
837            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
838                let strings: Vec<String> = self
839                    .into_iter()
840                    .map(|v| v.to_string_lossy().into_owned())
841                    .collect();
842                unsafe { strings.into_sexp_unchecked() }
843            }
844        }
845
846        $(#[$vec_option_meta])*
847        impl IntoR for Vec<Option<$owned_ty>> {
848            type Error = crate::into_r_error::IntoRError;
849            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
850                Ok(self.into_sexp())
851            }
852            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
853                Ok(unsafe { self.into_sexp_unchecked() })
854            }
855            fn into_sexp(self) -> crate::ffi::SEXP {
856                let strings: Vec<Option<String>> = self
857                    .into_iter()
858                    .map(|opt| opt.map(|v| v.to_string_lossy().into_owned()))
859                    .collect();
860                strings.into_sexp()
861            }
862            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
863                let strings: Vec<Option<String>> = self
864                    .into_iter()
865                    .map(|opt| opt.map(|v| v.to_string_lossy().into_owned()))
866                    .collect();
867                unsafe { strings.into_sexp_unchecked() }
868            }
869        }
870    };
871}
872
873impl_lossy_string_into_r!(
874    /// Convert `PathBuf` to R character scalar.
875    ///
876    /// On Unix, paths that are not valid UTF-8 will produce lossy output
877    /// (invalid sequences replaced with U+FFFD).
878    owned: PathBuf;
879    /// Convert `&Path` to R character scalar.
880    ref: &std::path::Path;
881    /// Convert `Option<PathBuf>` to R: Some(path) -> character, None -> NA_character_.
882    option: PathBuf;
883    /// Convert `Vec<PathBuf>` to R character vector.
884    vec: PathBuf;
885    /// Convert `Vec<Option<PathBuf>>` to R character vector with NA support.
886    vec_option: PathBuf;
887);
888
889impl_lossy_string_into_r!(
890    /// Convert `OsString` to R character scalar.
891    ///
892    /// On Unix, strings that are not valid UTF-8 will produce lossy output
893    /// (invalid sequences replaced with U+FFFD).
894    owned: OsString;
895    /// Convert `&OsStr` to R character scalar.
896    ref: &std::ffi::OsStr;
897    /// Convert `Option<OsString>` to R: Some(s) -> character, None -> NA_character_.
898    option: OsString;
899    /// Convert `Vec<OsString>` to R character vector.
900    vec: OsString;
901    /// Convert `Vec<Option<OsString>>` to R character vector with NA support.
902    vec_option: OsString;
903);
904// endregion
905
906// region: Set coercion for non-native types (i8, i16, u16 → i32)
907
908/// Macro for `HashSet<T>`/`BTreeSet<T>` where `T` coerces to i32 (R's native integer type).
909macro_rules! impl_set_coerce_into_r {
910    ($from:ty) => {
911        impl IntoR for HashSet<$from> {
912            type Error = std::convert::Infallible;
913            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
914                Ok(self.into_sexp())
915            }
916            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
917                self.try_into_sexp()
918            }
919            fn into_sexp(self) -> crate::ffi::SEXP {
920                let vec: Vec<i32> = self.into_iter().map(|x| i32::from(x)).collect();
921                vec.into_sexp()
922            }
923        }
924
925        impl IntoR for BTreeSet<$from> {
926            type Error = std::convert::Infallible;
927            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
928                Ok(self.into_sexp())
929            }
930            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
931                self.try_into_sexp()
932            }
933            fn into_sexp(self) -> crate::ffi::SEXP {
934                let vec: Vec<i32> = self.into_iter().map(|x| i32::from(x)).collect();
935                vec.into_sexp()
936            }
937        }
938    };
939}
940
941// Sub-i32 integer types in sets coerce to i32 (R's INTSXP)
942impl_set_coerce_into_r!(i8);
943impl_set_coerce_into_r!(i16);
944impl_set_coerce_into_r!(u16);
945// endregion
946
947// region: Option<Collection> conversions
948//
949// These return NULL (R_NilValue) for None, and the converted collection for Some.
950// This differs from Option<scalar> which returns NA for None.
951
952/// Convert `Option<Vec<T>>` to R: Some(vec) → vector, None → NULL.
953impl<T: crate::ffi::RNativeType> IntoR for Option<Vec<T>> {
954    type Error = std::convert::Infallible;
955    #[inline]
956    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
957        Ok(self.into_sexp())
958    }
959    #[inline]
960    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
961        Ok(unsafe { self.into_sexp_unchecked() })
962    }
963    #[inline]
964    fn into_sexp(self) -> crate::ffi::SEXP {
965        match self {
966            Some(v) => v.into_sexp(),
967            None => crate::ffi::SEXP::nil(),
968        }
969    }
970    #[inline]
971    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
972        match self {
973            Some(v) => unsafe { v.into_sexp_unchecked() },
974            None => crate::ffi::SEXP::nil(),
975        }
976    }
977}
978
979/// Convert `Option<Vec<String>>` to R: Some(vec) → character vector, None → NULL.
980impl IntoR for Option<Vec<String>> {
981    type Error = crate::into_r_error::IntoRError;
982    #[inline]
983    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
984        Ok(self.into_sexp())
985    }
986    #[inline]
987    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
988        Ok(unsafe { self.into_sexp_unchecked() })
989    }
990    #[inline]
991    fn into_sexp(self) -> crate::ffi::SEXP {
992        match self {
993            Some(v) => v.into_sexp(),
994            None => crate::ffi::SEXP::nil(),
995        }
996    }
997    #[inline]
998    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
999        match self {
1000            Some(v) => unsafe { v.into_sexp_unchecked() },
1001            None => crate::ffi::SEXP::nil(),
1002        }
1003    }
1004}
1005
1006/// Convert `Option<HashMap<String, V>>` to R: Some(map) -> named list, None -> NULL.
1007impl<V: IntoR> IntoR for Option<HashMap<String, V>> {
1008    type Error = crate::into_r_error::IntoRError;
1009    #[inline]
1010    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1011        Ok(self.into_sexp())
1012    }
1013    #[inline]
1014    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1015        Ok(unsafe { self.into_sexp_unchecked() })
1016    }
1017    #[inline]
1018    fn into_sexp(self) -> crate::ffi::SEXP {
1019        match self {
1020            Some(v) => v.into_sexp(),
1021            None => crate::ffi::SEXP::nil(),
1022        }
1023    }
1024    #[inline]
1025    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1026        match self {
1027            Some(v) => unsafe { v.into_sexp_unchecked() },
1028            None => crate::ffi::SEXP::nil(),
1029        }
1030    }
1031}
1032
1033/// Convert `Option<BTreeMap<String, V>>` to R: Some(map) -> named list, None -> NULL.
1034impl<V: IntoR> IntoR for Option<BTreeMap<String, V>> {
1035    type Error = crate::into_r_error::IntoRError;
1036    #[inline]
1037    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1038        Ok(self.into_sexp())
1039    }
1040    #[inline]
1041    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1042        Ok(unsafe { self.into_sexp_unchecked() })
1043    }
1044    #[inline]
1045    fn into_sexp(self) -> crate::ffi::SEXP {
1046        match self {
1047            Some(v) => v.into_sexp(),
1048            None => crate::ffi::SEXP::nil(),
1049        }
1050    }
1051    #[inline]
1052    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1053        match self {
1054            Some(v) => unsafe { v.into_sexp_unchecked() },
1055            None => crate::ffi::SEXP::nil(),
1056        }
1057    }
1058}
1059
1060/// Convert `Option<HashSet<T>>` to R: Some(set) -> vector, None -> NULL.
1061impl<T: crate::ffi::RNativeType + Eq + Hash> IntoR for Option<HashSet<T>> {
1062    type Error = std::convert::Infallible;
1063    #[inline]
1064    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1065        Ok(self.into_sexp())
1066    }
1067    #[inline]
1068    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1069        Ok(unsafe { self.into_sexp_unchecked() })
1070    }
1071    #[inline]
1072    fn into_sexp(self) -> crate::ffi::SEXP {
1073        match self {
1074            Some(v) => v.into_sexp(),
1075            None => crate::ffi::SEXP::nil(),
1076        }
1077    }
1078    #[inline]
1079    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1080        match self {
1081            Some(v) => unsafe { v.into_sexp_unchecked() },
1082            None => crate::ffi::SEXP::nil(),
1083        }
1084    }
1085}
1086
1087/// Convert `Option<BTreeSet<T>>` to R: Some(set) -> vector, None -> NULL.
1088impl<T: crate::ffi::RNativeType + Ord> IntoR for Option<BTreeSet<T>> {
1089    type Error = std::convert::Infallible;
1090    #[inline]
1091    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1092        Ok(self.into_sexp())
1093    }
1094    #[inline]
1095    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1096        Ok(unsafe { self.into_sexp_unchecked() })
1097    }
1098    #[inline]
1099    fn into_sexp(self) -> crate::ffi::SEXP {
1100        match self {
1101            Some(v) => v.into_sexp(),
1102            None => crate::ffi::SEXP::nil(),
1103        }
1104    }
1105    #[inline]
1106    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1107        match self {
1108            Some(v) => unsafe { v.into_sexp_unchecked() },
1109            None => crate::ffi::SEXP::nil(),
1110        }
1111    }
1112}
1113
1114macro_rules! impl_option_collection_into_r {
1115    ($(#[$meta:meta])* $ty:ty) => {
1116        $(#[$meta])*
1117        impl IntoR for Option<$ty> {
1118            type Error = crate::into_r_error::IntoRError;
1119            #[inline]
1120            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1121                Ok(self.into_sexp())
1122            }
1123            #[inline]
1124            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1125                Ok(unsafe { self.into_sexp_unchecked() })
1126            }
1127            #[inline]
1128            fn into_sexp(self) -> crate::ffi::SEXP {
1129                match self {
1130                    Some(v) => v.into_sexp(),
1131                    None => crate::ffi::SEXP::nil(),
1132                }
1133            }
1134            #[inline]
1135            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1136                match self {
1137                    Some(v) => unsafe { v.into_sexp_unchecked() },
1138                    None => crate::ffi::SEXP::nil(),
1139                }
1140            }
1141        }
1142    };
1143}
1144
1145impl_option_collection_into_r!(
1146    /// Convert `Option<HashSet<String>>` to R: Some(set) -> character vector, None -> NULL.
1147    HashSet<String>
1148);
1149impl_option_collection_into_r!(
1150    /// Convert `Option<BTreeSet<String>>` to R: Some(set) -> character vector, None -> NULL.
1151    BTreeSet<String>
1152);
1153
1154/// Helper: allocate STRSXP and fill from a string iterator (checked).
1155pub(crate) fn str_iter_to_strsxp<'a>(
1156    iter: impl ExactSizeIterator<Item = &'a str>,
1157) -> crate::ffi::SEXP {
1158    unsafe {
1159        let n: crate::ffi::R_xlen_t = iter
1160            .len()
1161            .try_into()
1162            .expect("string vec length exceeds isize::MAX");
1163        let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::STRSXP, n));
1164        for (i, s) in iter.enumerate() {
1165            let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1166            let charsxp = str_to_charsxp(s);
1167            sexp.set_string_elt(idx, charsxp);
1168        }
1169        *sexp
1170    }
1171}
1172
1173/// Helper: allocate STRSXP and fill from a string iterator (unchecked).
1174pub(crate) unsafe fn str_iter_to_strsxp_unchecked<'a>(
1175    iter: impl ExactSizeIterator<Item = &'a str>,
1176) -> crate::ffi::SEXP {
1177    unsafe {
1178        let n: crate::ffi::R_xlen_t = iter
1179            .len()
1180            .try_into()
1181            .expect("string vec length exceeds isize::MAX");
1182        let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector_unchecked(
1183            crate::ffi::SEXPTYPE::STRSXP,
1184            n,
1185        ));
1186        for (i, s) in iter.enumerate() {
1187            let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1188            let charsxp = str_to_charsxp_unchecked(s);
1189            sexp.set_string_elt_unchecked(idx, charsxp);
1190        }
1191        *sexp
1192    }
1193}
1194
1195/// Convert `Vec<String>` to R character vector (STRSXP).
1196impl IntoR for Vec<String> {
1197    type Error = std::convert::Infallible;
1198    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1199        Ok(self.into_sexp())
1200    }
1201    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1202        Ok(unsafe { self.into_sexp_unchecked() })
1203    }
1204    fn into_sexp(self) -> crate::ffi::SEXP {
1205        str_iter_to_strsxp(self.iter().map(|s| s.as_str()))
1206    }
1207
1208    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1209        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_str())) }
1210    }
1211}
1212
1213/// Convert `&[String]` to R character vector (STRSXP).
1214impl IntoR for &[String] {
1215    type Error = std::convert::Infallible;
1216    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1217        Ok(self.into_sexp())
1218    }
1219    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1220        Ok(unsafe { self.into_sexp_unchecked() })
1221    }
1222    fn into_sexp(self) -> crate::ffi::SEXP {
1223        str_iter_to_strsxp(self.iter().map(|s| s.as_str()))
1224    }
1225
1226    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1227        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_str())) }
1228    }
1229}
1230
1231/// Convert `Box<[String]>` to R character vector (STRSXP).
1232impl IntoR for Box<[String]> {
1233    type Error = std::convert::Infallible;
1234    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1235        Ok(self.into_sexp())
1236    }
1237    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1238        Ok(unsafe { self.into_sexp_unchecked() })
1239    }
1240    fn into_sexp(self) -> crate::ffi::SEXP {
1241        str_iter_to_strsxp(self.iter().map(|s| s.as_str()))
1242    }
1243    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1244        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_str())) }
1245    }
1246}
1247
1248/// Convert &[&str] to R character vector (STRSXP).
1249impl IntoR for &[&str] {
1250    type Error = std::convert::Infallible;
1251    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1252        Ok(self.into_sexp())
1253    }
1254    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1255        Ok(unsafe { self.into_sexp_unchecked() })
1256    }
1257    fn into_sexp(self) -> crate::ffi::SEXP {
1258        str_iter_to_strsxp(self.iter().copied())
1259    }
1260
1261    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1262        unsafe { str_iter_to_strsxp_unchecked(self.iter().copied()) }
1263    }
1264}
1265
1266/// Convert `Vec<Cow<'_, str>>` to R character vector (STRSXP).
1267impl IntoR for Vec<std::borrow::Cow<'_, str>> {
1268    type Error = std::convert::Infallible;
1269    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1270        Ok(self.into_sexp())
1271    }
1272    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1273        Ok(unsafe { self.into_sexp_unchecked() })
1274    }
1275    fn into_sexp(self) -> crate::ffi::SEXP {
1276        str_iter_to_strsxp(self.iter().map(|s| s.as_ref()))
1277    }
1278    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1279        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_ref())) }
1280    }
1281}
1282
1283/// Convert `Box<[Cow<'_, str>]>` to R character vector (STRSXP).
1284impl IntoR for Box<[std::borrow::Cow<'_, str>]> {
1285    type Error = std::convert::Infallible;
1286    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1287        Ok(self.into_sexp())
1288    }
1289    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1290        Ok(unsafe { self.into_sexp_unchecked() })
1291    }
1292    fn into_sexp(self) -> crate::ffi::SEXP {
1293        str_iter_to_strsxp(self.iter().map(|s| s.as_ref()))
1294    }
1295    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1296        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_ref())) }
1297    }
1298}
1299
1300/// Convert `Vec<Option<Cow<'_, str>>>` to R character vector with NA support.
1301///
1302/// `None` values become `NA_character_` in R.
1303impl IntoR for Vec<Option<std::borrow::Cow<'_, str>>> {
1304    type Error = std::convert::Infallible;
1305    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1306        Ok(self.into_sexp())
1307    }
1308    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1309        Ok(unsafe { self.into_sexp_unchecked() })
1310    }
1311    fn into_sexp(self) -> crate::ffi::SEXP {
1312        unsafe {
1313            let n: crate::ffi::R_xlen_t = self
1314                .len()
1315                .try_into()
1316                .expect("vec length exceeds isize::MAX");
1317            let sexp =
1318                OwnedProtect::new(crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::STRSXP, n));
1319            for (i, opt_s) in self.iter().enumerate() {
1320                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1321                let charsxp = match opt_s {
1322                    Some(s) => str_to_charsxp(s.as_ref()),
1323                    None => crate::ffi::SEXP::na_string(),
1324                };
1325                sexp.set_string_elt(idx, charsxp);
1326            }
1327            *sexp
1328        }
1329    }
1330    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1331        unsafe {
1332            let n: crate::ffi::R_xlen_t = self
1333                .len()
1334                .try_into()
1335                .expect("vec length exceeds isize::MAX");
1336            let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector_unchecked(
1337                crate::ffi::SEXPTYPE::STRSXP,
1338                n,
1339            ));
1340            for (i, opt_s) in self.iter().enumerate() {
1341                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1342                let charsxp = match opt_s {
1343                    Some(s) => str_to_charsxp_unchecked(s.as_ref()),
1344                    None => crate::ffi::SEXP::na_string(),
1345                };
1346                sexp.set_string_elt_unchecked(idx, charsxp);
1347            }
1348            *sexp
1349        }
1350    }
1351}
1352
1353/// Convert `Vec<&str>` to R character vector (STRSXP).
1354impl IntoR for Vec<&str> {
1355    type Error = std::convert::Infallible;
1356    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1357        Ok(self.into_sexp())
1358    }
1359    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1360        Ok(unsafe { self.into_sexp_unchecked() })
1361    }
1362    fn into_sexp(self) -> crate::ffi::SEXP {
1363        self.as_slice().into_sexp()
1364    }
1365
1366    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1367        unsafe { self.as_slice().into_sexp_unchecked() }
1368    }
1369}
1370// endregion
1371
1372// region: Nested vector conversions (list of vectors)
1373
1374/// Convert `Vec<Vec<T>>` to R list of vectors (VECSXP of typed vectors).
1375impl<T> IntoR for Vec<Vec<T>>
1376where
1377    T: crate::ffi::RNativeType,
1378{
1379    type Error = std::convert::Infallible;
1380    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1381        Ok(self.into_sexp())
1382    }
1383    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1384        Ok(unsafe { self.into_sexp_unchecked() })
1385    }
1386    fn into_sexp(self) -> crate::ffi::SEXP {
1387        unsafe {
1388            let n = self.len();
1389            let list =
1390                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
1391            crate::ffi::Rf_protect(list);
1392
1393            for (i, inner) in self.into_iter().enumerate() {
1394                let inner_sexp = inner.into_sexp();
1395                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
1396            }
1397
1398            crate::ffi::Rf_unprotect(1);
1399            list
1400        }
1401    }
1402
1403    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1404        unsafe {
1405            let n = self.len();
1406            let list = crate::ffi::Rf_allocVector_unchecked(
1407                crate::ffi::SEXPTYPE::VECSXP,
1408                n as crate::ffi::R_xlen_t,
1409            );
1410            crate::ffi::Rf_protect(list);
1411
1412            for (i, inner) in self.into_iter().enumerate() {
1413                let inner_sexp = inner.into_sexp_unchecked();
1414                list.set_vector_elt_unchecked(i as crate::ffi::R_xlen_t, inner_sexp);
1415            }
1416
1417            crate::ffi::Rf_unprotect(1);
1418            list
1419        }
1420    }
1421}
1422
1423/// Convert `Vec<Vec<String>>` to R list of character vectors.
1424impl IntoR for Vec<Vec<String>> {
1425    type Error = std::convert::Infallible;
1426    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1427        Ok(self.into_sexp())
1428    }
1429    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1430        Ok(unsafe { self.into_sexp_unchecked() })
1431    }
1432    fn into_sexp(self) -> crate::ffi::SEXP {
1433        unsafe {
1434            let n = self.len();
1435            let list =
1436                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
1437            crate::ffi::Rf_protect(list);
1438
1439            for (i, inner) in self.into_iter().enumerate() {
1440                let inner_sexp = inner.into_sexp();
1441                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
1442            }
1443
1444            crate::ffi::Rf_unprotect(1);
1445            list
1446        }
1447    }
1448
1449    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1450        unsafe {
1451            let n = self.len();
1452            let list = crate::ffi::Rf_allocVector_unchecked(
1453                crate::ffi::SEXPTYPE::VECSXP,
1454                n as crate::ffi::R_xlen_t,
1455            );
1456            crate::ffi::Rf_protect(list);
1457
1458            for (i, inner) in self.into_iter().enumerate() {
1459                let inner_sexp = inner.into_sexp_unchecked();
1460                list.set_vector_elt_unchecked(i as crate::ffi::R_xlen_t, inner_sexp);
1461            }
1462
1463            crate::ffi::Rf_unprotect(1);
1464            list
1465        }
1466    }
1467}
1468// endregion
1469
1470// region: NA-aware vector conversions
1471
1472/// Macro for NA-aware `Vec<Option<T>> → R` vector conversions.
1473///
1474/// Uses `alloc_r_vector` to get a mutable slice, then fills it.
1475macro_rules! impl_vec_option_into_r {
1476    ($t:ty, $na_value:expr) => {
1477        impl IntoR for Vec<Option<$t>> {
1478            type Error = std::convert::Infallible;
1479            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1480                Ok(self.into_sexp())
1481            }
1482            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1483                Ok(unsafe { self.into_sexp_unchecked() })
1484            }
1485            fn into_sexp(self) -> crate::ffi::SEXP {
1486                unsafe {
1487                    let (sexp, dst) = alloc_r_vector::<$t>(self.len());
1488                    for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
1489                        *slot = val.unwrap_or($na_value);
1490                    }
1491                    sexp
1492                }
1493            }
1494
1495            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1496                unsafe {
1497                    let (sexp, dst) = alloc_r_vector_unchecked::<$t>(self.len());
1498                    for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
1499                        *slot = val.unwrap_or($na_value);
1500                    }
1501                    sexp
1502                }
1503            }
1504        }
1505    };
1506}
1507
1508impl_vec_option_into_r!(f64, NA_REAL); // NA_real_
1509impl_vec_option_into_r!(i32, NA_INTEGER); // NA_integer_
1510
1511/// Macro for NA-aware `Vec<Option<T>> → R` smart vector conversion.
1512/// Checks if all non-None values fit i32 → INTSXP, otherwise REALSXP.
1513///
1514/// Allocates the R vector directly and coerces in-place — no intermediate Vec.
1515macro_rules! impl_vec_option_smart_i64_into_r {
1516    ($t:ty, $fits_i32:expr) => {
1517        impl IntoR for Vec<Option<$t>> {
1518            type Error = std::convert::Infallible;
1519            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1520                Ok(self.into_sexp())
1521            }
1522            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1523                self.try_into_sexp()
1524            }
1525            fn into_sexp(self) -> crate::ffi::SEXP {
1526                unsafe {
1527                    if self.iter().all(|opt| match opt {
1528                        Some(x) => $fits_i32(*x),
1529                        None => true,
1530                    }) {
1531                        let (sexp, dst) = alloc_r_vector::<i32>(self.len());
1532                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
1533                            *slot = match val {
1534                                Some(x) => x as i32,
1535                                None => NA_INTEGER,
1536                            };
1537                        }
1538                        sexp
1539                    } else {
1540                        let (sexp, dst) = alloc_r_vector::<f64>(self.len());
1541                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
1542                            *slot = match val {
1543                                Some(x) => x as f64,
1544                                None => NA_REAL,
1545                            };
1546                        }
1547                        sexp
1548                    }
1549                }
1550            }
1551        }
1552    };
1553}
1554
1555// i32::MIN is NA_integer_ in R, so exclude it
1556impl_vec_option_smart_i64_into_r!(i64, |x: i64| x > i32::MIN as i64 && x <= i32::MAX as i64);
1557impl_vec_option_smart_i64_into_r!(u64, |x: u64| x <= i32::MAX as u64);
1558impl_vec_option_smart_i64_into_r!(isize, |x: isize| x > i32::MIN as isize
1559    && x <= i32::MAX as isize);
1560impl_vec_option_smart_i64_into_r!(usize, |x: usize| x <= i32::MAX as usize);
1561
1562/// Macro for `Vec<Option<T>>` where `T` coerces to a type with existing Option impl.
1563///
1564/// Delegates to the target type's `Vec<Option<$to>>` impl (which itself uses alloc_r_vector).
1565macro_rules! impl_vec_option_coerce_into_r {
1566    ($from:ty => $to:ty) => {
1567        impl IntoR for Vec<Option<$from>> {
1568            type Error = std::convert::Infallible;
1569            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1570                Ok(self.into_sexp())
1571            }
1572            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1573                self.try_into_sexp()
1574            }
1575            fn into_sexp(self) -> crate::ffi::SEXP {
1576                // Delegate to the target Option type's impl (coerce inline)
1577                let coerced: Vec<Option<$to>> = self
1578                    .into_iter()
1579                    .map(|opt| opt.map(|x| <$to>::from(x)))
1580                    .collect();
1581                coerced.into_sexp()
1582            }
1583        }
1584    };
1585}
1586
1587impl_vec_option_coerce_into_r!(i8 => i32);
1588impl_vec_option_coerce_into_r!(i16 => i32);
1589impl_vec_option_coerce_into_r!(u16 => i32);
1590impl_vec_option_coerce_into_r!(u32 => i64); // delegates to smart i64 path
1591impl_vec_option_coerce_into_r!(f32 => f64);
1592
1593/// Helper: allocate LGLSXP and fill from an i32 iterator (checked).
1594///
1595/// Uses `alloc_r_vector` — logical vectors are `RLogical` (repr(transparent) i32).
1596fn logical_iter_to_lglsxp(n: usize, iter: impl Iterator<Item = i32>) -> crate::ffi::SEXP {
1597    unsafe {
1598        let (sexp, dst) = alloc_r_vector::<crate::ffi::RLogical>(n);
1599        // RLogical is repr(transparent) over i32, safe to write i32 values.
1600        let dst_i32: &mut [i32] = std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<i32>(), n);
1601        for (slot, val) in dst_i32.iter_mut().zip(iter) {
1602            *slot = val;
1603        }
1604        sexp
1605    }
1606}
1607
1608/// Helper: allocate LGLSXP and fill from an i32 iterator (unchecked).
1609unsafe fn logical_iter_to_lglsxp_unchecked(
1610    n: usize,
1611    iter: impl Iterator<Item = i32>,
1612) -> crate::ffi::SEXP {
1613    unsafe {
1614        let (sexp, dst) = alloc_r_vector_unchecked::<crate::ffi::RLogical>(n);
1615        let dst_i32: &mut [i32] = std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<i32>(), n);
1616        for (slot, val) in dst_i32.iter_mut().zip(iter) {
1617            *slot = val;
1618        }
1619        sexp
1620    }
1621}
1622
1623/// Convert `Vec<bool>` to R logical vector.
1624impl IntoR for Vec<bool> {
1625    type Error = std::convert::Infallible;
1626    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1627        Ok(self.into_sexp())
1628    }
1629    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1630        Ok(unsafe { self.into_sexp_unchecked() })
1631    }
1632    fn into_sexp(self) -> crate::ffi::SEXP {
1633        let n = self.len();
1634        logical_iter_to_lglsxp(n, self.into_iter().map(i32::from))
1635    }
1636
1637    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1638        let n = self.len();
1639        unsafe { logical_iter_to_lglsxp_unchecked(n, self.into_iter().map(i32::from)) }
1640    }
1641}
1642
1643/// Convert `Box<[bool]>` to R logical vector.
1644impl IntoR for Box<[bool]> {
1645    type Error = std::convert::Infallible;
1646    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1647        Ok(self.into_sexp())
1648    }
1649    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1650        Ok(unsafe { self.into_sexp_unchecked() })
1651    }
1652    fn into_sexp(self) -> crate::ffi::SEXP {
1653        let n = self.len();
1654        logical_iter_to_lglsxp(n, self.iter().map(|&v| i32::from(v)))
1655    }
1656    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1657        let n = self.len();
1658        unsafe { logical_iter_to_lglsxp_unchecked(n, self.iter().map(|&v| i32::from(v))) }
1659    }
1660}
1661
1662/// Convert `&[bool]` to R logical vector.
1663impl IntoR for &[bool] {
1664    type Error = std::convert::Infallible;
1665    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1666        Ok(self.into_sexp())
1667    }
1668    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1669        Ok(unsafe { self.into_sexp_unchecked() })
1670    }
1671    fn into_sexp(self) -> crate::ffi::SEXP {
1672        let n = self.len();
1673        logical_iter_to_lglsxp(n, self.iter().map(|&v| i32::from(v)))
1674    }
1675
1676    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1677        let n = self.len();
1678        unsafe { logical_iter_to_lglsxp_unchecked(n, self.iter().map(|&v| i32::from(v))) }
1679    }
1680}
1681
1682macro_rules! impl_vec_option_logical_into_r {
1683    ($(#[$meta:meta])* $t:ty, $convert:expr) => {
1684        $(#[$meta])*
1685        impl IntoR for Vec<Option<$t>> {
1686            type Error = std::convert::Infallible;
1687            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1688                Ok(self.into_sexp())
1689            }
1690            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1691                Ok(unsafe { self.into_sexp_unchecked() })
1692            }
1693            fn into_sexp(self) -> crate::ffi::SEXP {
1694                let n = self.len();
1695                logical_iter_to_lglsxp(n, self.into_iter().map($convert))
1696            }
1697
1698            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1699                let n = self.len();
1700                unsafe { logical_iter_to_lglsxp_unchecked(n, self.into_iter().map($convert)) }
1701            }
1702        }
1703    };
1704}
1705
1706impl_vec_option_logical_into_r!(
1707    /// Convert `Vec<Option<bool>>` to R logical vector with NA support.
1708    bool,
1709    |v: Option<bool>| match v {
1710        Some(true) => 1,
1711        Some(false) => 0,
1712        None => NA_LOGICAL,
1713    }
1714);
1715impl_vec_option_logical_into_r!(
1716    /// Convert `Vec<Option<Rboolean>>` to R logical vector with NA support.
1717    crate::ffi::Rboolean,
1718    |v: Option<crate::ffi::Rboolean>| match v {
1719        Some(b) => b as i32,
1720        None => NA_LOGICAL,
1721    }
1722);
1723impl_vec_option_logical_into_r!(
1724    /// Convert `Vec<Option<RLogical>>` to R logical vector with NA support.
1725    crate::ffi::RLogical,
1726    |v: Option<crate::ffi::RLogical>| match v {
1727        Some(b) => b.to_i32(),
1728        None => NA_LOGICAL,
1729    }
1730);
1731
1732/// Convert `Vec<Option<String>>` to R character vector with NA support.
1733///
1734/// `None` values become `NA_character_` in R.
1735impl IntoR for Vec<Option<String>> {
1736    type Error = std::convert::Infallible;
1737    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1738        Ok(self.into_sexp())
1739    }
1740    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1741        Ok(unsafe { self.into_sexp_unchecked() })
1742    }
1743    fn into_sexp(self) -> crate::ffi::SEXP {
1744        unsafe {
1745            let n: crate::ffi::R_xlen_t = self
1746                .len()
1747                .try_into()
1748                .expect("vec length exceeds isize::MAX");
1749            let sexp =
1750                OwnedProtect::new(crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::STRSXP, n));
1751
1752            for (i, opt_s) in self.iter().enumerate() {
1753                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1754                let charsxp = match opt_s {
1755                    Some(s) => str_to_charsxp(s),
1756                    None => crate::ffi::SEXP::na_string(),
1757                };
1758                sexp.set_string_elt(idx, charsxp);
1759            }
1760
1761            *sexp
1762        }
1763    }
1764
1765    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1766        unsafe {
1767            let n: crate::ffi::R_xlen_t = self
1768                .len()
1769                .try_into()
1770                .expect("vec length exceeds isize::MAX");
1771            let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector_unchecked(
1772                crate::ffi::SEXPTYPE::STRSXP,
1773                n,
1774            ));
1775
1776            for (i, opt_s) in self.iter().enumerate() {
1777                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1778                let charsxp = match opt_s {
1779                    Some(s) => str_to_charsxp_unchecked(s),
1780                    None => crate::ffi::SEXP::na_string(),
1781                };
1782                sexp.set_string_elt_unchecked(idx, charsxp);
1783            }
1784
1785            *sexp
1786        }
1787    }
1788}
1789// endregion
1790
1791// region: Tuple to list conversions
1792
1793/// Macro to implement IntoR for tuples of various sizes.
1794/// Converts Rust tuples to unnamed R lists (VECSXP).
1795macro_rules! impl_tuple_into_r {
1796    // Base case: 2-tuple
1797    (($($T:ident),+), ($($idx:tt),+), $n:expr) => {
1798        impl<$($T: IntoR),+> IntoR for ($($T,)+) {
1799            type Error = std::convert::Infallible;
1800            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1801                Ok(self.into_sexp())
1802            }
1803            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1804                Ok(unsafe { self.into_sexp_unchecked() })
1805            }
1806            fn into_sexp(self) -> crate::ffi::SEXP {
1807                unsafe {
1808                    let list = crate::ffi::Rf_allocVector(
1809                        crate::ffi::SEXPTYPE::VECSXP,
1810                        $n as crate::ffi::R_xlen_t
1811                    );
1812                    crate::ffi::Rf_protect(list);
1813
1814                    $(
1815
1816                            list.set_vector_elt($idx as crate::ffi::R_xlen_t, self.$idx.into_sexp()
1817                        );
1818                    )+
1819
1820                    crate::ffi::Rf_unprotect(1);
1821                    list
1822                }
1823            }
1824
1825            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1826                unsafe {
1827                    let list = crate::ffi::Rf_allocVector_unchecked(
1828                        crate::ffi::SEXPTYPE::VECSXP,
1829                        $n as crate::ffi::R_xlen_t
1830                    );
1831                    crate::ffi::Rf_protect(list);
1832
1833                    $(
1834
1835                            list.set_vector_elt_unchecked($idx as crate::ffi::R_xlen_t, self.$idx.into_sexp_unchecked()
1836                        );
1837                    )+
1838
1839                    crate::ffi::Rf_unprotect(1);
1840                    list
1841                }
1842            }
1843        }
1844    };
1845}
1846
1847// Implement for tuples of sizes 2-8
1848impl_tuple_into_r!((A, B), (0, 1), 2);
1849impl_tuple_into_r!((A, B, C), (0, 1, 2), 3);
1850impl_tuple_into_r!((A, B, C, D), (0, 1, 2, 3), 4);
1851impl_tuple_into_r!((A, B, C, D, E), (0, 1, 2, 3, 4), 5);
1852impl_tuple_into_r!((A, B, C, D, E, F), (0, 1, 2, 3, 4, 5), 6);
1853impl_tuple_into_r!((A, B, C, D, E, F, G), (0, 1, 2, 3, 4, 5, 6), 7);
1854impl_tuple_into_r!((A, B, C, D, E, F, G, H), (0, 1, 2, 3, 4, 5, 6, 7), 8);
1855// endregion
1856
1857// region: ALTREP zero-copy extension trait
1858
1859/// Extension trait for ALTREP conversions.
1860///
1861/// This trait provides ergonomic methods for converting Rust types to R ALTREP
1862/// vectors without copying data. The data stays in Rust memory (wrapped in an
1863/// ExternalPtr) and R accesses it via ALTREP callbacks.
1864///
1865/// # Performance Characteristics
1866///
1867/// | Operation | Regular (IntoR) | ALTREP (IntoRAltrep) |
1868/// |-----------|-----------------|------------------------|
1869/// | Creation | O(n) copy | O(1) wrap |
1870/// | Memory | Duplicated in R | Single copy in Rust |
1871/// | Element access | Direct pointer | Callback (~10ns overhead) |
1872/// | DATAPTR ops | O(1) | O(1) if Vec/Box, N/A if lazy |
1873///
1874/// # When to Use ALTREP
1875///
1876/// **Good candidates**:
1877/// - ✅ Large vectors (>1000 elements)
1878/// - ✅ Lazy/computed data (avoid eager materialization)
1879/// - ✅ External data sources (files, databases, APIs)
1880/// - ✅ Data that might not be fully accessed by R
1881///
1882/// **Not recommended**:
1883/// - ❌ Small vectors (<100 elements) - copy overhead is negligible
1884/// - ❌ Data R will immediately modify (triggers copy anyway)
1885/// - ❌ Temporary results (extra indirection not worth it)
1886///
1887/// # Example
1888///
1889/// ```rust,ignore
1890/// use miniextendr_api::{miniextendr, IntoRAltrep, IntoR, ffi::SEXP};
1891///
1892/// #[miniextendr]
1893/// fn large_dataset() -> SEXP {
1894///     let data: Vec<f64> = (0..1_000_000).map(|i| i as f64).collect();
1895///
1896///     // Zero-copy: wraps pointer instead of copying 1M elements
1897///     data.into_sexp_altrep()
1898/// }
1899///
1900/// #[miniextendr]
1901/// fn small_result() -> SEXP {
1902///     let data = vec![1, 2, 3, 4, 5];
1903///
1904///     // Regular copy is fine for small data
1905///     data.into_sexp()
1906/// }
1907/// ```
1908pub trait IntoRAltrep {
1909    /// Convert to R SEXP using ALTREP zero-copy representation.
1910    ///
1911    /// This is equivalent to `Altrep(self).into_sexp()` but more discoverable
1912    /// and explicit about the zero-copy intent.
1913    fn into_sexp_altrep(self) -> crate::ffi::SEXP;
1914
1915    /// Convert to R SEXP using ALTREP, skipping debug thread assertions.
1916    ///
1917    /// # Safety
1918    ///
1919    /// Caller must ensure they are on R's main thread.
1920    unsafe fn into_sexp_altrep_unchecked(self) -> crate::ffi::SEXP
1921    where
1922        Self: Sized,
1923    {
1924        self.into_sexp_altrep()
1925    }
1926
1927    /// Create an `Altrep<Self>` wrapper.
1928    ///
1929    /// This returns the wrapper explicitly, allowing you to store it or
1930    /// further process it before conversion.
1931    fn into_altrep(self) -> Altrep<Self>
1932    where
1933        Self: Sized,
1934    {
1935        Altrep(self)
1936    }
1937}
1938
1939impl<T> IntoRAltrep for T
1940where
1941    T: crate::altrep::RegisterAltrep + crate::externalptr::TypedExternal,
1942{
1943    fn into_sexp_altrep(self) -> crate::ffi::SEXP {
1944        Altrep(self).into_sexp()
1945    }
1946
1947    unsafe fn into_sexp_altrep_unchecked(self) -> crate::ffi::SEXP {
1948        unsafe { Altrep(self).into_sexp_unchecked() }
1949    }
1950}
1951// endregion
1952
1953// region: Additional collection type conversions for DataFrameRow support
1954
1955/// Convert `Vec<Box<[T]>>` to R list of vectors (for RNativeType elements).
1956/// Each boxed slice becomes an R vector.
1957impl<T> IntoR for Vec<Box<[T]>>
1958where
1959    T: crate::ffi::RNativeType,
1960{
1961    type Error = std::convert::Infallible;
1962    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1963        Ok(self.into_sexp())
1964    }
1965    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1966        self.try_into_sexp()
1967    }
1968    fn into_sexp(self) -> crate::ffi::SEXP {
1969        unsafe {
1970            let n = self.len();
1971            let list =
1972                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
1973            crate::ffi::Rf_protect(list);
1974
1975            for (i, boxed_slice) in self.into_iter().enumerate() {
1976                let vec: Vec<T> = boxed_slice.into_vec();
1977                let inner_sexp = vec.into_sexp();
1978                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
1979            }
1980
1981            crate::ffi::Rf_unprotect(1);
1982            list
1983        }
1984    }
1985}
1986
1987/// Convert `Vec<Box<[String]>>` to R list of character vectors.
1988impl IntoR for Vec<Box<[String]>> {
1989    type Error = std::convert::Infallible;
1990    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1991        Ok(self.into_sexp())
1992    }
1993    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1994        self.try_into_sexp()
1995    }
1996    fn into_sexp(self) -> crate::ffi::SEXP {
1997        unsafe {
1998            let n = self.len();
1999            let list =
2000                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
2001            crate::ffi::Rf_protect(list);
2002
2003            for (i, boxed_slice) in self.into_iter().enumerate() {
2004                let vec: Vec<String> = boxed_slice.into_vec();
2005                let inner_sexp = vec.into_sexp();
2006                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
2007            }
2008
2009            crate::ffi::Rf_unprotect(1);
2010            list
2011        }
2012    }
2013}
2014
2015/// Convert `Vec<[T; N]>` to R list of vectors.
2016/// Each array becomes an R vector.
2017impl<T, const N: usize> IntoR for Vec<[T; N]>
2018where
2019    T: crate::ffi::RNativeType,
2020{
2021    type Error = std::convert::Infallible;
2022    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2023        Ok(self.into_sexp())
2024    }
2025    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2026        self.try_into_sexp()
2027    }
2028    fn into_sexp(self) -> crate::ffi::SEXP {
2029        unsafe {
2030            let len = self.len();
2031            let list = crate::ffi::Rf_allocVector(
2032                crate::ffi::SEXPTYPE::VECSXP,
2033                len as crate::ffi::R_xlen_t,
2034            );
2035            crate::ffi::Rf_protect(list);
2036
2037            for (i, array) in self.into_iter().enumerate() {
2038                let vec: Vec<T> = array.into();
2039                let inner_sexp = vec.into_sexp();
2040                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
2041            }
2042
2043            crate::ffi::Rf_unprotect(1);
2044            list
2045        }
2046    }
2047}
2048
2049/// Helper: convert a Vec of IntoR items to an R list (VECSXP).
2050fn vec_of_into_r_to_list<T: IntoR>(items: Vec<T>) -> crate::ffi::SEXP {
2051    unsafe {
2052        let n = items.len();
2053        let list = OwnedProtect::new(crate::ffi::Rf_allocVector(
2054            crate::ffi::SEXPTYPE::VECSXP,
2055            n as crate::ffi::R_xlen_t,
2056        ));
2057        for (i, item) in items.into_iter().enumerate() {
2058            list.get()
2059                .set_vector_elt(i as crate::ffi::R_xlen_t, item.into_sexp());
2060        }
2061        *list
2062    }
2063}
2064
2065/// Convert `Vec<HashSet<T>>` to R list of vectors (for RNativeType elements).
2066/// Each HashSet becomes an R vector (unordered).
2067impl<T: crate::ffi::RNativeType> IntoR for Vec<std::collections::HashSet<T>> {
2068    type Error = std::convert::Infallible;
2069    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2070        Ok(self.into_sexp())
2071    }
2072    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2073        self.try_into_sexp()
2074    }
2075    fn into_sexp(self) -> crate::ffi::SEXP {
2076        let converted: Vec<Vec<T>> = self.into_iter().map(|s| s.into_iter().collect()).collect();
2077        vec_of_into_r_to_list(converted)
2078    }
2079}
2080
2081/// Convert `Vec<BTreeSet<T>>` to R list of vectors (for RNativeType elements).
2082/// Each BTreeSet becomes an R vector (sorted).
2083impl<T: crate::ffi::RNativeType> IntoR for Vec<std::collections::BTreeSet<T>> {
2084    type Error = std::convert::Infallible;
2085    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2086        Ok(self.into_sexp())
2087    }
2088    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2089        self.try_into_sexp()
2090    }
2091    fn into_sexp(self) -> crate::ffi::SEXP {
2092        let converted: Vec<Vec<T>> = self.into_iter().map(|s| s.into_iter().collect()).collect();
2093        vec_of_into_r_to_list(converted)
2094    }
2095}
2096
2097/// Convert `Vec<HashSet<String>>` to R list of character vectors.
2098impl IntoR for Vec<std::collections::HashSet<String>> {
2099    type Error = std::convert::Infallible;
2100    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2101        Ok(self.into_sexp())
2102    }
2103    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2104        self.try_into_sexp()
2105    }
2106    fn into_sexp(self) -> crate::ffi::SEXP {
2107        let converted: Vec<Vec<String>> =
2108            self.into_iter().map(|s| s.into_iter().collect()).collect();
2109        vec_of_into_r_to_list(converted)
2110    }
2111}
2112
2113/// Convert `Vec<BTreeSet<String>>` to R list of character vectors.
2114impl IntoR for Vec<std::collections::BTreeSet<String>> {
2115    type Error = std::convert::Infallible;
2116    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2117        Ok(self.into_sexp())
2118    }
2119    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2120        self.try_into_sexp()
2121    }
2122    fn into_sexp(self) -> crate::ffi::SEXP {
2123        let converted: Vec<Vec<String>> =
2124            self.into_iter().map(|s| s.into_iter().collect()).collect();
2125        vec_of_into_r_to_list(converted)
2126    }
2127}
2128
2129macro_rules! impl_vec_map_into_r {
2130    ($(#[$meta:meta])* $map_ty:ident) => {
2131        $(#[$meta])*
2132        impl<V: IntoR> IntoR for Vec<$map_ty<String, V>> {
2133            type Error = std::convert::Infallible;
2134            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2135                Ok(self.into_sexp())
2136            }
2137            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2138                self.try_into_sexp()
2139            }
2140            fn into_sexp(self) -> crate::ffi::SEXP {
2141                vec_of_maps_to_list(self)
2142            }
2143        }
2144    };
2145}
2146
2147impl_vec_map_into_r!(
2148    /// Convert `Vec<HashMap<String, V>>` to R list of named lists.
2149    HashMap
2150);
2151impl_vec_map_into_r!(
2152    /// Convert `Vec<BTreeMap<String, V>>` to R list of named lists.
2153    BTreeMap
2154);
2155
2156/// Helper to convert a Vec of map-like types to an R list of named lists.
2157fn vec_of_maps_to_list<T: IntoR>(vec: Vec<T>) -> crate::ffi::SEXP {
2158    unsafe {
2159        let n = vec.len();
2160        let list =
2161            crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
2162        crate::ffi::Rf_protect(list);
2163
2164        for (i, map) in vec.into_iter().enumerate() {
2165            list.set_vector_elt(i as crate::ffi::R_xlen_t, map.into_sexp());
2166        }
2167
2168        crate::ffi::Rf_unprotect(1);
2169        list
2170    }
2171}
2172// endregion