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 SEXP::scalar_* methods.
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(crate::ffi::SEXP::$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                crate::ffi::SEXP::$checked(self)
194            }
195            #[inline]
196            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
197                unsafe { crate::ffi::SEXP::$unchecked(self) }
198            }
199        }
200    };
201}
202
203impl_scalar_into_r!(i32, scalar_integer, scalar_integer_unchecked);
204impl_scalar_into_r!(f64, scalar_real, scalar_real_unchecked);
205impl_scalar_into_r!(u8, scalar_raw, scalar_raw_unchecked);
206impl_scalar_into_r!(
207    crate::ffi::Rcomplex,
208    scalar_complex,
209    scalar_complex_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::SEXP::scalar_complex_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
285// Concrete IntoR impls for Vec<T> where T: RNativeType.
286//
287// These are written as concrete impls rather than a blanket
288// `impl<T: RNativeType> IntoR for Vec<T>` to avoid a coherence conflict
289// with `impl<T: MatchArg> IntoR for Vec<T>` in match_arg.rs.
290// Both blankets would target `Vec<_>` for the same foreign trait, and the
291// Rust coherence checker (E0119) rejects them even when the bounds are
292// disjoint in practice, because negative trait bounds are not stable.
293macro_rules! impl_into_r_vec_native {
294    ($t:ty) => {
295        impl IntoR for Vec<$t> {
296            type Error = std::convert::Infallible;
297            #[inline]
298            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
299                Ok(unsafe { vec_to_sexp(&self) })
300            }
301            #[inline]
302            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
303                Ok(unsafe { self.into_sexp_unchecked() })
304            }
305            #[inline]
306            fn into_sexp(self) -> crate::ffi::SEXP {
307                unsafe { vec_to_sexp(&self) }
308            }
309            #[inline]
310            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
311                unsafe { vec_to_sexp_unchecked(&self) }
312            }
313        }
314    };
315}
316
317impl_into_r_vec_native!(i32);
318impl_into_r_vec_native!(f64);
319impl_into_r_vec_native!(u8);
320impl_into_r_vec_native!(crate::ffi::RLogical);
321impl_into_r_vec_native!(crate::ffi::Rcomplex);
322
323impl<T> IntoR for &[T]
324where
325    T: crate::ffi::RNativeType,
326{
327    type Error = std::convert::Infallible;
328    #[inline]
329    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
330        Ok(unsafe { vec_to_sexp(self) })
331    }
332    #[inline]
333    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
334        Ok(unsafe { self.into_sexp_unchecked() })
335    }
336    #[inline]
337    fn into_sexp(self) -> crate::ffi::SEXP {
338        unsafe { vec_to_sexp(self) }
339    }
340    #[inline]
341    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
342        unsafe { vec_to_sexp_unchecked(self) }
343    }
344}
345
346impl<T> IntoR for Box<[T]>
347where
348    T: crate::ffi::RNativeType,
349{
350    type Error = std::convert::Infallible;
351    #[inline]
352    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
353        Ok(unsafe { vec_to_sexp(&self) })
354    }
355    #[inline]
356    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
357        Ok(unsafe { vec_to_sexp_unchecked(&self) })
358    }
359    #[inline]
360    fn into_sexp(self) -> crate::ffi::SEXP {
361        unsafe { vec_to_sexp(&self) }
362    }
363    #[inline]
364    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
365        unsafe { vec_to_sexp_unchecked(&self) }
366    }
367}
368
369// region: R vector allocation helpers
370//
371// These are the ONLY place in the codebase that should call Rf_allocVector
372// for typed vectors and obtain a mutable data slice. All conversion code
373// uses these helpers instead of raw FFI pointer arithmetic.
374
375/// Allocate an R vector of type `T` with `n` elements and return `(SEXP, &mut [T])`.
376///
377/// The returned SEXP is **unprotected** — caller must protect via `Rf_protect`,
378/// `OwnedProtect`, or `ProtectScope` before any further R allocation.
379///
380/// # Safety
381///
382/// Must be called from R's main thread.
383#[inline]
384pub(crate) unsafe fn alloc_r_vector<T: crate::ffi::RNativeType>(
385    n: usize,
386) -> (crate::ffi::SEXP, &'static mut [T]) {
387    unsafe {
388        let sexp = crate::ffi::Rf_allocVector(T::SEXP_TYPE, n as crate::ffi::R_xlen_t);
389        let slice = crate::from_r::r_slice_mut(T::dataptr_mut(sexp), n);
390        (sexp, slice)
391    }
392}
393
394/// Allocate an R vector (unchecked FFI variant).
395///
396/// # Safety
397///
398/// Must be called from R's main thread.
399#[inline]
400pub(crate) unsafe fn alloc_r_vector_unchecked<T: crate::ffi::RNativeType>(
401    n: usize,
402) -> (crate::ffi::SEXP, &'static mut [T]) {
403    unsafe {
404        let sexp = crate::ffi::Rf_allocVector_unchecked(T::SEXP_TYPE, n as crate::ffi::R_xlen_t);
405        let slice = crate::from_r::r_slice_mut(T::dataptr_mut(sexp), n);
406        (sexp, slice)
407    }
408}
409
410// endregion
411
412/// Convert a slice to an R vector (checked) using `copy_from_slice`.
413#[inline]
414unsafe fn vec_to_sexp<T: crate::ffi::RNativeType>(slice: &[T]) -> crate::ffi::SEXP {
415    unsafe {
416        let (sexp, dst) = alloc_r_vector::<T>(slice.len());
417        dst.copy_from_slice(slice);
418        sexp
419    }
420}
421
422/// Convert a slice to an R vector (unchecked) using `copy_from_slice`.
423#[inline]
424unsafe fn vec_to_sexp_unchecked<T: crate::ffi::RNativeType>(slice: &[T]) -> crate::ffi::SEXP {
425    unsafe {
426        let (sexp, dst) = alloc_r_vector_unchecked::<T>(slice.len());
427        dst.copy_from_slice(slice);
428        sexp
429    }
430}
431// endregion
432
433// region: Vec coercion for non-native types (i8, i16, u16 → i32; f32 → f64)
434
435/// Macro for `Vec<T>` where `T` coerces to a native R type.
436///
437/// Allocates the R vector directly and coerces in-place — no intermediate Vec.
438macro_rules! impl_vec_coerce_into_r {
439    ($from:ty => $to:ty) => {
440        impl IntoR for Vec<$from> {
441            type Error = std::convert::Infallible;
442            #[inline]
443            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
444                Ok(self.into_sexp())
445            }
446            #[inline]
447            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
448                Ok(unsafe { self.into_sexp_unchecked() })
449            }
450            #[inline]
451            fn into_sexp(self) -> crate::ffi::SEXP {
452                unsafe {
453                    let (sexp, dst) = alloc_r_vector::<$to>(self.len());
454                    for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
455                        *slot = <$to>::from(val);
456                    }
457                    sexp
458                }
459            }
460            #[inline]
461            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
462                unsafe {
463                    let (sexp, dst) = alloc_r_vector_unchecked::<$to>(self.len());
464                    for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
465                        *slot = <$to>::from(val);
466                    }
467                    sexp
468                }
469            }
470        }
471
472        impl IntoR for &[$from] {
473            type Error = std::convert::Infallible;
474            #[inline]
475            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
476                Ok(self.into_sexp())
477            }
478            #[inline]
479            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
480                Ok(unsafe { self.into_sexp_unchecked() })
481            }
482            #[inline]
483            fn into_sexp(self) -> crate::ffi::SEXP {
484                unsafe {
485                    let (sexp, dst) = alloc_r_vector::<$to>(self.len());
486                    for (slot, &val) in dst.iter_mut().zip(self.iter()) {
487                        *slot = <$to>::from(val);
488                    }
489                    sexp
490                }
491            }
492            #[inline]
493            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
494                unsafe {
495                    let (sexp, dst) = alloc_r_vector_unchecked::<$to>(self.len());
496                    for (slot, &val) in dst.iter_mut().zip(self.iter()) {
497                        *slot = <$to>::from(val);
498                    }
499                    sexp
500                }
501            }
502        }
503    };
504}
505
506// Sub-i32 integer types coerce to i32 (R's INTSXP)
507impl_vec_coerce_into_r!(i8 => i32);
508impl_vec_coerce_into_r!(i16 => i32);
509impl_vec_coerce_into_r!(u16 => i32);
510
511// f32 coerces to f64 (R's REALSXP)
512impl_vec_coerce_into_r!(f32 => f64);
513
514// i64/u64/isize/usize: smart conversion (INTSXP when all fit, else REALSXP)
515//
516// Allocates the R vector directly and coerces in-place — no intermediate Vec.
517macro_rules! impl_vec_smart_i64_into_r {
518    ($t:ty, $fits_i32:expr) => {
519        impl IntoR for Vec<$t> {
520            type Error = std::convert::Infallible;
521            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
522                Ok(self.into_sexp())
523            }
524            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
525                Ok(unsafe { self.into_sexp_unchecked() })
526            }
527            fn into_sexp(self) -> crate::ffi::SEXP {
528                unsafe {
529                    if self.iter().all(|&x| $fits_i32(x)) {
530                        let (sexp, dst) = alloc_r_vector::<i32>(self.len());
531                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
532                            // fits_i32 guard verified range
533                            *slot = val as i32;
534                        }
535                        sexp
536                    } else {
537                        let (sexp, dst) = alloc_r_vector::<f64>(self.len());
538                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
539                            // R has no 64-bit integer; f64 loses precision > 2^53
540                            *slot = val as f64;
541                        }
542                        sexp
543                    }
544                }
545            }
546            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
547                unsafe {
548                    if self.iter().all(|&x| $fits_i32(x)) {
549                        let (sexp, dst) = alloc_r_vector_unchecked::<i32>(self.len());
550                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
551                            // fits_i32 guard verified range
552                            *slot = val as i32;
553                        }
554                        sexp
555                    } else {
556                        let (sexp, dst) = alloc_r_vector_unchecked::<f64>(self.len());
557                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
558                            // R has no 64-bit integer; f64 loses precision > 2^53
559                            *slot = val as f64;
560                        }
561                        sexp
562                    }
563                }
564            }
565        }
566    };
567}
568
569// i32::MIN is NA_integer_ in R, so exclude it
570impl_vec_smart_i64_into_r!(i64, |x: i64| x > i32::MIN as i64 && x <= i32::MAX as i64);
571impl_vec_smart_i64_into_r!(u64, |x: u64| x <= i32::MAX as u64);
572impl_vec_smart_i64_into_r!(isize, |x: isize| x > i32::MIN as isize
573    && x <= i32::MAX as isize);
574impl_vec_smart_i64_into_r!(usize, |x: usize| x <= i32::MAX as usize);
575// endregion
576
577mod altrep;
578mod collections;
579mod result;
580
581pub use altrep::*;
582pub use result::*;
583
584// region: Fixed-size array conversions
585
586/// Blanket impl for `[T; N]` where T: RNativeType.
587///
588/// Enables direct conversion of fixed-size arrays to R vectors.
589/// Useful for SHA hashes, fixed-size byte patterns, etc.
590impl<T: crate::ffi::RNativeType, const N: usize> IntoR for [T; N] {
591    type Error = std::convert::Infallible;
592    #[inline]
593    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
594        Ok(self.as_slice().into_sexp())
595    }
596    #[inline]
597    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
598        Ok(unsafe { self.into_sexp_unchecked() })
599    }
600    #[inline]
601    fn into_sexp(self) -> crate::ffi::SEXP {
602        self.as_slice().into_sexp()
603    }
604    #[inline]
605    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
606        unsafe { self.as_slice().into_sexp_unchecked() }
607    }
608}
609// endregion
610
611// region: VecDeque conversions
612
613use std::collections::VecDeque;
614
615/// Convert `VecDeque<T>` to R vector where T: RNativeType.
616impl<T> IntoR for VecDeque<T>
617where
618    T: crate::ffi::RNativeType,
619{
620    type Error = std::convert::Infallible;
621    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
622        Ok(self.into_sexp())
623    }
624    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
625        Ok(unsafe { self.into_sexp_unchecked() })
626    }
627    fn into_sexp(self) -> crate::ffi::SEXP {
628        let vec: Vec<T> = self.into_iter().collect();
629        vec.into_sexp()
630    }
631    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
632        let vec: Vec<T> = self.into_iter().collect();
633        unsafe { vec.into_sexp_unchecked() }
634    }
635}
636// endregion
637
638// region: BinaryHeap conversions
639
640use std::collections::BinaryHeap;
641
642/// Convert `BinaryHeap<T>` to R vector where T: RNativeType + Ord.
643///
644/// The heap is drained into a vector (destroying the heap property).
645/// Elements are returned in arbitrary order, not sorted.
646impl<T> IntoR for BinaryHeap<T>
647where
648    T: crate::ffi::RNativeType + Ord,
649{
650    type Error = std::convert::Infallible;
651    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
652        Ok(self.into_vec().into_sexp())
653    }
654    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
655        Ok(unsafe { self.into_sexp_unchecked() })
656    }
657    fn into_sexp(self) -> crate::ffi::SEXP {
658        self.into_vec().into_sexp()
659    }
660    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
661        unsafe { self.into_vec().into_sexp_unchecked() }
662    }
663}
664// endregion
665
666// region: Cow conversions
667
668use std::borrow::Cow;
669
670/// Try SEXP pointer recovery for a borrowed Cow slice.
671#[inline]
672#[allow(clippy::ptr_arg)] // Need &Cow to inspect Borrowed vs Owned variant
673fn try_recover_cow_slice<T: crate::ffi::RNativeType>(
674    cow: &Cow<'_, [T]>,
675) -> Option<crate::ffi::SEXP> {
676    if let Cow::Borrowed(slice) = cow {
677        unsafe {
678            crate::r_memory::try_recover_r_sexp(
679                slice.as_ptr() as *const u8,
680                T::SEXP_TYPE,
681                slice.len(),
682            )
683        }
684    } else {
685        None
686    }
687}
688
689/// Convert `Cow<'_, [T]>` to R vector where T: RNativeType.
690///
691/// For `Cow::Borrowed` slices that came from R (e.g., via `TryFromSexp`),
692/// SEXP pointer recovery is attempted — if the borrowed data points into
693/// an R vector, the original SEXP is returned without copying. Otherwise
694/// falls through to the standard copy path.
695impl<T> IntoR for Cow<'_, [T]>
696where
697    T: crate::ffi::RNativeType + Clone,
698{
699    type Error = std::convert::Infallible;
700    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
701        Ok(self.into_sexp())
702    }
703    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
704        Ok(unsafe { self.into_sexp_unchecked() })
705    }
706    fn into_sexp(self) -> crate::ffi::SEXP {
707        if let Some(sexp) = try_recover_cow_slice(&self) {
708            return sexp;
709        }
710        self.as_ref().into_sexp()
711    }
712    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
713        if let Some(sexp) = try_recover_cow_slice(&self) {
714            return sexp;
715        }
716        unsafe { self.as_ref().into_sexp_unchecked() }
717    }
718}
719
720/// Convert `Cow<'_, str>` to R character scalar.
721impl IntoR for Cow<'_, str> {
722    type Error = crate::into_r_error::IntoRError;
723    #[inline]
724    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
725        self.as_ref().try_into_sexp()
726    }
727    #[inline]
728    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
729        Ok(unsafe { self.into_sexp_unchecked() })
730    }
731    #[inline]
732    fn into_sexp(self) -> crate::ffi::SEXP {
733        self.as_ref().into_sexp()
734    }
735    #[inline]
736    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
737        unsafe { self.as_ref().into_sexp_unchecked() }
738    }
739}
740// endregion
741
742// region: Box conversions (skipped - conflicts with IntoExternalPtr blanket impl)
743//
744// We can't add `impl<T: IntoR> IntoR for Box<T>` because it conflicts with
745// the blanket impl `impl<T: IntoExternalPtr> IntoR for T`. If downstream
746// crates implement `IntoExternalPtr for Box<SomeType>`, we'd have overlapping
747// impls. Users can manually unbox with `*boxed_value` before conversion.
748// endregion
749
750// region: PathBuf / OsString conversions
751
752use std::ffi::OsString;
753use std::path::PathBuf;
754
755/// Generate IntoR impls for types with `to_string_lossy()` (owned scalar, ref scalar,
756/// Option, Vec, Vec<Option>). Used for PathBuf/&Path and OsString/&OsStr.
757macro_rules! impl_lossy_string_into_r {
758    (
759        $(#[$owned_meta:meta])*
760        owned: $owned_ty:ty;
761        $(#[$ref_meta:meta])*
762        ref: $ref_ty:ty;
763        $(#[$option_meta:meta])*
764        option: $opt_ty:ty;
765        $(#[$vec_meta:meta])*
766        vec: $vec_ty:ty;
767        $(#[$vec_option_meta:meta])*
768        vec_option: $vec_opt_ty:ty;
769    ) => {
770        $(#[$owned_meta])*
771        impl IntoR for $owned_ty {
772            type Error = crate::into_r_error::IntoRError;
773            #[inline]
774            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
775                self.to_string_lossy().into_owned().try_into_sexp()
776            }
777            #[inline]
778            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
779                Ok(unsafe { self.into_sexp_unchecked() })
780            }
781            #[inline]
782            fn into_sexp(self) -> crate::ffi::SEXP {
783                self.to_string_lossy().into_owned().into_sexp()
784            }
785            #[inline]
786            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
787                unsafe { self.to_string_lossy().into_owned().into_sexp_unchecked() }
788            }
789        }
790
791        $(#[$ref_meta])*
792        impl IntoR for $ref_ty {
793            type Error = crate::into_r_error::IntoRError;
794            #[inline]
795            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
796                self.to_string_lossy().into_owned().try_into_sexp()
797            }
798            #[inline]
799            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
800                Ok(unsafe { self.into_sexp_unchecked() })
801            }
802            #[inline]
803            fn into_sexp(self) -> crate::ffi::SEXP {
804                self.to_string_lossy().into_owned().into_sexp()
805            }
806            #[inline]
807            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
808                unsafe { self.to_string_lossy().into_owned().into_sexp_unchecked() }
809            }
810        }
811
812        $(#[$option_meta])*
813        impl IntoR for Option<$owned_ty> {
814            type Error = crate::into_r_error::IntoRError;
815            #[inline]
816            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
817                self.map(|v| v.to_string_lossy().into_owned()).try_into_sexp()
818            }
819            #[inline]
820            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
821                Ok(unsafe { self.into_sexp_unchecked() })
822            }
823            #[inline]
824            fn into_sexp(self) -> crate::ffi::SEXP {
825                self.map(|v| v.to_string_lossy().into_owned()).into_sexp()
826            }
827            #[inline]
828            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
829                unsafe {
830                    self.map(|v| v.to_string_lossy().into_owned())
831                        .into_sexp_unchecked()
832                }
833            }
834        }
835
836        $(#[$vec_meta])*
837        impl IntoR for Vec<$owned_ty> {
838            type Error = crate::into_r_error::IntoRError;
839            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
840                Ok(self.into_sexp())
841            }
842            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
843                Ok(unsafe { self.into_sexp_unchecked() })
844            }
845            fn into_sexp(self) -> crate::ffi::SEXP {
846                let strings: Vec<String> = self
847                    .into_iter()
848                    .map(|v| v.to_string_lossy().into_owned())
849                    .collect();
850                strings.into_sexp()
851            }
852            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
853                let strings: Vec<String> = self
854                    .into_iter()
855                    .map(|v| v.to_string_lossy().into_owned())
856                    .collect();
857                unsafe { strings.into_sexp_unchecked() }
858            }
859        }
860
861        $(#[$vec_option_meta])*
862        impl IntoR for Vec<Option<$owned_ty>> {
863            type Error = crate::into_r_error::IntoRError;
864            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
865                Ok(self.into_sexp())
866            }
867            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
868                Ok(unsafe { self.into_sexp_unchecked() })
869            }
870            fn into_sexp(self) -> crate::ffi::SEXP {
871                let strings: Vec<Option<String>> = self
872                    .into_iter()
873                    .map(|opt| opt.map(|v| v.to_string_lossy().into_owned()))
874                    .collect();
875                strings.into_sexp()
876            }
877            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
878                let strings: Vec<Option<String>> = self
879                    .into_iter()
880                    .map(|opt| opt.map(|v| v.to_string_lossy().into_owned()))
881                    .collect();
882                unsafe { strings.into_sexp_unchecked() }
883            }
884        }
885    };
886}
887
888impl_lossy_string_into_r!(
889    /// Convert `PathBuf` to R character scalar.
890    ///
891    /// On Unix, paths that are not valid UTF-8 will produce lossy output
892    /// (invalid sequences replaced with U+FFFD).
893    owned: PathBuf;
894    /// Convert `&Path` to R character scalar.
895    ref: &std::path::Path;
896    /// Convert `Option<PathBuf>` to R: Some(path) -> character, None -> NA_character_.
897    option: PathBuf;
898    /// Convert `Vec<PathBuf>` to R character vector.
899    vec: PathBuf;
900    /// Convert `Vec<Option<PathBuf>>` to R character vector with NA support.
901    vec_option: PathBuf;
902);
903
904impl_lossy_string_into_r!(
905    /// Convert `OsString` to R character scalar.
906    ///
907    /// On Unix, strings that are not valid UTF-8 will produce lossy output
908    /// (invalid sequences replaced with U+FFFD).
909    owned: OsString;
910    /// Convert `&OsStr` to R character scalar.
911    ref: &std::ffi::OsStr;
912    /// Convert `Option<OsString>` to R: Some(s) -> character, None -> NA_character_.
913    option: OsString;
914    /// Convert `Vec<OsString>` to R character vector.
915    vec: OsString;
916    /// Convert `Vec<Option<OsString>>` to R character vector with NA support.
917    vec_option: OsString;
918);
919// endregion
920
921// region: Set coercion for non-native types (i8, i16, u16 → i32)
922
923/// Macro for `HashSet<T>`/`BTreeSet<T>` where `T` coerces to i32 (R's native integer type).
924macro_rules! impl_set_coerce_into_r {
925    ($from:ty) => {
926        impl IntoR for HashSet<$from> {
927            type Error = std::convert::Infallible;
928            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
929                Ok(self.into_sexp())
930            }
931            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
932                self.try_into_sexp()
933            }
934            fn into_sexp(self) -> crate::ffi::SEXP {
935                let vec: Vec<i32> = self.into_iter().map(|x| i32::from(x)).collect();
936                vec.into_sexp()
937            }
938        }
939
940        impl IntoR for BTreeSet<$from> {
941            type Error = std::convert::Infallible;
942            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
943                Ok(self.into_sexp())
944            }
945            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
946                self.try_into_sexp()
947            }
948            fn into_sexp(self) -> crate::ffi::SEXP {
949                let vec: Vec<i32> = self.into_iter().map(|x| i32::from(x)).collect();
950                vec.into_sexp()
951            }
952        }
953    };
954}
955
956// Sub-i32 integer types in sets coerce to i32 (R's INTSXP)
957impl_set_coerce_into_r!(i8);
958impl_set_coerce_into_r!(i16);
959impl_set_coerce_into_r!(u16);
960// endregion
961
962// region: Option<Collection> conversions
963//
964// These return NULL (R_NilValue) for None, and the converted collection for Some.
965// This differs from Option<scalar> which returns NA for None.
966
967/// Convert `Option<Vec<T>>` to R: Some(vec) → vector, None → NULL.
968impl<T: crate::ffi::RNativeType> IntoR for Option<Vec<T>> {
969    type Error = std::convert::Infallible;
970    #[inline]
971    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
972        Ok(self.into_sexp())
973    }
974    #[inline]
975    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
976        Ok(unsafe { self.into_sexp_unchecked() })
977    }
978    #[inline]
979    fn into_sexp(self) -> crate::ffi::SEXP {
980        match self {
981            Some(v) => v.into_sexp(),
982            None => crate::ffi::SEXP::nil(),
983        }
984    }
985    #[inline]
986    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
987        match self {
988            Some(v) => unsafe { v.into_sexp_unchecked() },
989            None => crate::ffi::SEXP::nil(),
990        }
991    }
992}
993
994/// Convert `Option<Vec<String>>` to R: Some(vec) → character vector, None → NULL.
995impl IntoR for Option<Vec<String>> {
996    type Error = crate::into_r_error::IntoRError;
997    #[inline]
998    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
999        Ok(self.into_sexp())
1000    }
1001    #[inline]
1002    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1003        Ok(unsafe { self.into_sexp_unchecked() })
1004    }
1005    #[inline]
1006    fn into_sexp(self) -> crate::ffi::SEXP {
1007        match self {
1008            Some(v) => v.into_sexp(),
1009            None => crate::ffi::SEXP::nil(),
1010        }
1011    }
1012    #[inline]
1013    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1014        match self {
1015            Some(v) => unsafe { v.into_sexp_unchecked() },
1016            None => crate::ffi::SEXP::nil(),
1017        }
1018    }
1019}
1020
1021/// Convert `Option<HashMap<String, V>>` to R: Some(map) -> named list, None -> NULL.
1022impl<V: IntoR> IntoR for Option<HashMap<String, V>> {
1023    type Error = crate::into_r_error::IntoRError;
1024    #[inline]
1025    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1026        Ok(self.into_sexp())
1027    }
1028    #[inline]
1029    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1030        Ok(unsafe { self.into_sexp_unchecked() })
1031    }
1032    #[inline]
1033    fn into_sexp(self) -> crate::ffi::SEXP {
1034        match self {
1035            Some(v) => v.into_sexp(),
1036            None => crate::ffi::SEXP::nil(),
1037        }
1038    }
1039    #[inline]
1040    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1041        match self {
1042            Some(v) => unsafe { v.into_sexp_unchecked() },
1043            None => crate::ffi::SEXP::nil(),
1044        }
1045    }
1046}
1047
1048/// Convert `Option<BTreeMap<String, V>>` to R: Some(map) -> named list, None -> NULL.
1049impl<V: IntoR> IntoR for Option<BTreeMap<String, V>> {
1050    type Error = crate::into_r_error::IntoRError;
1051    #[inline]
1052    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1053        Ok(self.into_sexp())
1054    }
1055    #[inline]
1056    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1057        Ok(unsafe { self.into_sexp_unchecked() })
1058    }
1059    #[inline]
1060    fn into_sexp(self) -> crate::ffi::SEXP {
1061        match self {
1062            Some(v) => v.into_sexp(),
1063            None => crate::ffi::SEXP::nil(),
1064        }
1065    }
1066    #[inline]
1067    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1068        match self {
1069            Some(v) => unsafe { v.into_sexp_unchecked() },
1070            None => crate::ffi::SEXP::nil(),
1071        }
1072    }
1073}
1074
1075/// Convert `Option<HashSet<T>>` to R: Some(set) -> vector, None -> NULL.
1076impl<T: crate::ffi::RNativeType + Eq + Hash> IntoR for Option<HashSet<T>> {
1077    type Error = std::convert::Infallible;
1078    #[inline]
1079    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1080        Ok(self.into_sexp())
1081    }
1082    #[inline]
1083    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1084        Ok(unsafe { self.into_sexp_unchecked() })
1085    }
1086    #[inline]
1087    fn into_sexp(self) -> crate::ffi::SEXP {
1088        match self {
1089            Some(v) => v.into_sexp(),
1090            None => crate::ffi::SEXP::nil(),
1091        }
1092    }
1093    #[inline]
1094    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1095        match self {
1096            Some(v) => unsafe { v.into_sexp_unchecked() },
1097            None => crate::ffi::SEXP::nil(),
1098        }
1099    }
1100}
1101
1102/// Convert `Option<BTreeSet<T>>` to R: Some(set) -> vector, None -> NULL.
1103impl<T: crate::ffi::RNativeType + Ord> IntoR for Option<BTreeSet<T>> {
1104    type Error = std::convert::Infallible;
1105    #[inline]
1106    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1107        Ok(self.into_sexp())
1108    }
1109    #[inline]
1110    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1111        Ok(unsafe { self.into_sexp_unchecked() })
1112    }
1113    #[inline]
1114    fn into_sexp(self) -> crate::ffi::SEXP {
1115        match self {
1116            Some(v) => v.into_sexp(),
1117            None => crate::ffi::SEXP::nil(),
1118        }
1119    }
1120    #[inline]
1121    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1122        match self {
1123            Some(v) => unsafe { v.into_sexp_unchecked() },
1124            None => crate::ffi::SEXP::nil(),
1125        }
1126    }
1127}
1128
1129macro_rules! impl_option_collection_into_r {
1130    ($(#[$meta:meta])* $ty:ty) => {
1131        $(#[$meta])*
1132        impl IntoR for Option<$ty> {
1133            type Error = crate::into_r_error::IntoRError;
1134            #[inline]
1135            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1136                Ok(self.into_sexp())
1137            }
1138            #[inline]
1139            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1140                Ok(unsafe { self.into_sexp_unchecked() })
1141            }
1142            #[inline]
1143            fn into_sexp(self) -> crate::ffi::SEXP {
1144                match self {
1145                    Some(v) => v.into_sexp(),
1146                    None => crate::ffi::SEXP::nil(),
1147                }
1148            }
1149            #[inline]
1150            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1151                match self {
1152                    Some(v) => unsafe { v.into_sexp_unchecked() },
1153                    None => crate::ffi::SEXP::nil(),
1154                }
1155            }
1156        }
1157    };
1158}
1159
1160impl_option_collection_into_r!(
1161    /// Convert `Option<HashSet<String>>` to R: Some(set) -> character vector, None -> NULL.
1162    HashSet<String>
1163);
1164impl_option_collection_into_r!(
1165    /// Convert `Option<BTreeSet<String>>` to R: Some(set) -> character vector, None -> NULL.
1166    BTreeSet<String>
1167);
1168
1169/// Helper: allocate STRSXP and fill from a string iterator (checked).
1170pub(crate) fn str_iter_to_strsxp<'a>(
1171    iter: impl ExactSizeIterator<Item = &'a str>,
1172) -> crate::ffi::SEXP {
1173    unsafe {
1174        let n: crate::ffi::R_xlen_t = iter
1175            .len()
1176            .try_into()
1177            .expect("string vec length exceeds isize::MAX");
1178        let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::STRSXP, n));
1179        for (i, s) in iter.enumerate() {
1180            let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1181            let charsxp = str_to_charsxp(s);
1182            sexp.set_string_elt(idx, charsxp);
1183        }
1184        *sexp
1185    }
1186}
1187
1188/// Helper: allocate STRSXP and fill from a string iterator (unchecked).
1189pub(crate) unsafe fn str_iter_to_strsxp_unchecked<'a>(
1190    iter: impl ExactSizeIterator<Item = &'a str>,
1191) -> crate::ffi::SEXP {
1192    unsafe {
1193        let n: crate::ffi::R_xlen_t = iter
1194            .len()
1195            .try_into()
1196            .expect("string vec length exceeds isize::MAX");
1197        let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector_unchecked(
1198            crate::ffi::SEXPTYPE::STRSXP,
1199            n,
1200        ));
1201        for (i, s) in iter.enumerate() {
1202            let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1203            let charsxp = str_to_charsxp_unchecked(s);
1204            sexp.set_string_elt_unchecked(idx, charsxp);
1205        }
1206        *sexp
1207    }
1208}
1209
1210/// Convert `Vec<String>` to R character vector (STRSXP).
1211impl IntoR for Vec<String> {
1212    type Error = std::convert::Infallible;
1213    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1214        Ok(self.into_sexp())
1215    }
1216    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1217        Ok(unsafe { self.into_sexp_unchecked() })
1218    }
1219    fn into_sexp(self) -> crate::ffi::SEXP {
1220        str_iter_to_strsxp(self.iter().map(|s| s.as_str()))
1221    }
1222
1223    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1224        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_str())) }
1225    }
1226}
1227
1228/// Convert `&[String]` to R character vector (STRSXP).
1229impl IntoR for &[String] {
1230    type Error = std::convert::Infallible;
1231    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1232        Ok(self.into_sexp())
1233    }
1234    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1235        Ok(unsafe { self.into_sexp_unchecked() })
1236    }
1237    fn into_sexp(self) -> crate::ffi::SEXP {
1238        str_iter_to_strsxp(self.iter().map(|s| s.as_str()))
1239    }
1240
1241    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1242        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_str())) }
1243    }
1244}
1245
1246/// Convert `Box<[String]>` to R character vector (STRSXP).
1247impl IntoR for Box<[String]> {
1248    type Error = std::convert::Infallible;
1249    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1250        Ok(self.into_sexp())
1251    }
1252    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1253        Ok(unsafe { self.into_sexp_unchecked() })
1254    }
1255    fn into_sexp(self) -> crate::ffi::SEXP {
1256        str_iter_to_strsxp(self.iter().map(|s| s.as_str()))
1257    }
1258    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1259        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_str())) }
1260    }
1261}
1262
1263/// Convert &[&str] to R character vector (STRSXP).
1264impl IntoR for &[&str] {
1265    type Error = std::convert::Infallible;
1266    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1267        Ok(self.into_sexp())
1268    }
1269    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1270        Ok(unsafe { self.into_sexp_unchecked() })
1271    }
1272    fn into_sexp(self) -> crate::ffi::SEXP {
1273        str_iter_to_strsxp(self.iter().copied())
1274    }
1275
1276    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1277        unsafe { str_iter_to_strsxp_unchecked(self.iter().copied()) }
1278    }
1279}
1280
1281/// Convert `Vec<Cow<'_, str>>` to R character vector (STRSXP).
1282impl IntoR for Vec<std::borrow::Cow<'_, str>> {
1283    type Error = std::convert::Infallible;
1284    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1285        Ok(self.into_sexp())
1286    }
1287    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1288        Ok(unsafe { self.into_sexp_unchecked() })
1289    }
1290    fn into_sexp(self) -> crate::ffi::SEXP {
1291        str_iter_to_strsxp(self.iter().map(|s| s.as_ref()))
1292    }
1293    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1294        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_ref())) }
1295    }
1296}
1297
1298/// Convert `Box<[Cow<'_, str>]>` to R character vector (STRSXP).
1299impl IntoR for Box<[std::borrow::Cow<'_, str>]> {
1300    type Error = std::convert::Infallible;
1301    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1302        Ok(self.into_sexp())
1303    }
1304    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1305        Ok(unsafe { self.into_sexp_unchecked() })
1306    }
1307    fn into_sexp(self) -> crate::ffi::SEXP {
1308        str_iter_to_strsxp(self.iter().map(|s| s.as_ref()))
1309    }
1310    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1311        unsafe { str_iter_to_strsxp_unchecked(self.iter().map(|s| s.as_ref())) }
1312    }
1313}
1314
1315/// Convert `Vec<Option<Cow<'_, str>>>` to R character vector with NA support.
1316///
1317/// `None` values become `NA_character_` in R.
1318impl IntoR for Vec<Option<std::borrow::Cow<'_, str>>> {
1319    type Error = std::convert::Infallible;
1320    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1321        Ok(self.into_sexp())
1322    }
1323    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1324        Ok(unsafe { self.into_sexp_unchecked() })
1325    }
1326    fn into_sexp(self) -> crate::ffi::SEXP {
1327        unsafe {
1328            let n: crate::ffi::R_xlen_t = self
1329                .len()
1330                .try_into()
1331                .expect("vec length exceeds isize::MAX");
1332            let sexp =
1333                OwnedProtect::new(crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::STRSXP, n));
1334            for (i, opt_s) in self.iter().enumerate() {
1335                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1336                let charsxp = match opt_s {
1337                    Some(s) => str_to_charsxp(s.as_ref()),
1338                    None => crate::ffi::SEXP::na_string(),
1339                };
1340                sexp.set_string_elt(idx, charsxp);
1341            }
1342            *sexp
1343        }
1344    }
1345    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1346        unsafe {
1347            let n: crate::ffi::R_xlen_t = self
1348                .len()
1349                .try_into()
1350                .expect("vec length exceeds isize::MAX");
1351            let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector_unchecked(
1352                crate::ffi::SEXPTYPE::STRSXP,
1353                n,
1354            ));
1355            for (i, opt_s) in self.iter().enumerate() {
1356                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1357                let charsxp = match opt_s {
1358                    Some(s) => str_to_charsxp_unchecked(s.as_ref()),
1359                    None => crate::ffi::SEXP::na_string(),
1360                };
1361                sexp.set_string_elt_unchecked(idx, charsxp);
1362            }
1363            *sexp
1364        }
1365    }
1366}
1367
1368// region: Vec<Option<borrowed string>>
1369/// Convert `Vec<Option<&str>>` to R character vector with NA support.
1370///
1371/// `None` values become `NA_character_` in R. Borrowed analogue of `Vec<Option<String>>`.
1372impl IntoR for Vec<Option<&str>> {
1373    type Error = std::convert::Infallible;
1374    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1375        Ok(self.into_sexp())
1376    }
1377    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1378        Ok(unsafe { self.into_sexp_unchecked() })
1379    }
1380    fn into_sexp(self) -> crate::ffi::SEXP {
1381        unsafe {
1382            let n: crate::ffi::R_xlen_t = self
1383                .len()
1384                .try_into()
1385                .expect("vec length exceeds isize::MAX");
1386            let sexp =
1387                OwnedProtect::new(crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::STRSXP, n));
1388            for (i, opt_s) in self.iter().enumerate() {
1389                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1390                let charsxp = match opt_s {
1391                    Some(s) => str_to_charsxp(s),
1392                    None => crate::ffi::SEXP::na_string(),
1393                };
1394                sexp.set_string_elt(idx, charsxp);
1395            }
1396            *sexp
1397        }
1398    }
1399    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1400        unsafe {
1401            let n: crate::ffi::R_xlen_t = self
1402                .len()
1403                .try_into()
1404                .expect("vec length exceeds isize::MAX");
1405            let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector_unchecked(
1406                crate::ffi::SEXPTYPE::STRSXP,
1407                n,
1408            ));
1409            for (i, opt_s) in self.iter().enumerate() {
1410                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1411                let charsxp = match opt_s {
1412                    Some(s) => str_to_charsxp_unchecked(s),
1413                    None => crate::ffi::SEXP::na_string(),
1414                };
1415                sexp.set_string_elt_unchecked(idx, charsxp);
1416            }
1417            *sexp
1418        }
1419    }
1420}
1421// endregion
1422
1423/// Convert `Vec<&str>` to R character vector (STRSXP).
1424impl IntoR for Vec<&str> {
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        self.as_slice().into_sexp()
1434    }
1435
1436    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1437        unsafe { self.as_slice().into_sexp_unchecked() }
1438    }
1439}
1440// endregion
1441
1442// region: Nested vector conversions (list of vectors)
1443
1444/// Convert `Vec<Vec<T>>` to R list of vectors (VECSXP of typed vectors).
1445impl<T> IntoR for Vec<Vec<T>>
1446where
1447    T: crate::ffi::RNativeType,
1448{
1449    type Error = std::convert::Infallible;
1450    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1451        Ok(self.into_sexp())
1452    }
1453    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1454        Ok(unsafe { self.into_sexp_unchecked() })
1455    }
1456    fn into_sexp(self) -> crate::ffi::SEXP {
1457        unsafe {
1458            let n = self.len();
1459            let list =
1460                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
1461            crate::ffi::Rf_protect(list);
1462
1463            for (i, inner) in self.into_iter().enumerate() {
1464                let inner_sexp = inner.into_sexp();
1465                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
1466            }
1467
1468            crate::ffi::Rf_unprotect(1);
1469            list
1470        }
1471    }
1472
1473    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1474        unsafe {
1475            let n = self.len();
1476            let list = crate::ffi::Rf_allocVector_unchecked(
1477                crate::ffi::SEXPTYPE::VECSXP,
1478                n as crate::ffi::R_xlen_t,
1479            );
1480            crate::ffi::Rf_protect(list);
1481
1482            for (i, inner) in self.into_iter().enumerate() {
1483                let inner_sexp = inner.into_sexp_unchecked();
1484                list.set_vector_elt_unchecked(i as crate::ffi::R_xlen_t, inner_sexp);
1485            }
1486
1487            crate::ffi::Rf_unprotect(1);
1488            list
1489        }
1490    }
1491}
1492
1493/// Convert `Vec<&[T]>` to R list of typed vectors (VECSXP).
1494///
1495/// Borrowed analogue of `Vec<Vec<T>>` — each slice is copied into a fresh R vector.
1496impl<T: crate::ffi::RNativeType> IntoR for Vec<&[T]> {
1497    type Error = std::convert::Infallible;
1498    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1499        Ok(self.into_sexp())
1500    }
1501    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1502        Ok(unsafe { self.into_sexp_unchecked() })
1503    }
1504    fn into_sexp(self) -> crate::ffi::SEXP {
1505        unsafe {
1506            let n = self.len();
1507            let list =
1508                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
1509            crate::ffi::Rf_protect(list);
1510            for (i, inner) in self.into_iter().enumerate() {
1511                let inner_sexp = inner.into_sexp();
1512                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
1513            }
1514            crate::ffi::Rf_unprotect(1);
1515            list
1516        }
1517    }
1518    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1519        unsafe {
1520            let n = self.len();
1521            let list = crate::ffi::Rf_allocVector_unchecked(
1522                crate::ffi::SEXPTYPE::VECSXP,
1523                n as crate::ffi::R_xlen_t,
1524            );
1525            crate::ffi::Rf_protect(list);
1526            for (i, inner) in self.into_iter().enumerate() {
1527                let inner_sexp = inner.into_sexp_unchecked();
1528                list.set_vector_elt_unchecked(i as crate::ffi::R_xlen_t, inner_sexp);
1529            }
1530            crate::ffi::Rf_unprotect(1);
1531            list
1532        }
1533    }
1534}
1535
1536/// Convert `Vec<&[String]>` to R list of character vectors.
1537///
1538/// Borrowed analogue of `Vec<Vec<String>>`.
1539impl IntoR for Vec<&[String]> {
1540    type Error = std::convert::Infallible;
1541    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1542        Ok(self.into_sexp())
1543    }
1544    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1545        Ok(unsafe { self.into_sexp_unchecked() })
1546    }
1547    fn into_sexp(self) -> crate::ffi::SEXP {
1548        unsafe {
1549            let n = self.len();
1550            let list =
1551                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
1552            crate::ffi::Rf_protect(list);
1553            for (i, inner) in self.into_iter().enumerate() {
1554                let inner_sexp = inner.into_sexp();
1555                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
1556            }
1557            crate::ffi::Rf_unprotect(1);
1558            list
1559        }
1560    }
1561    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1562        unsafe {
1563            let n = self.len();
1564            let list = crate::ffi::Rf_allocVector_unchecked(
1565                crate::ffi::SEXPTYPE::VECSXP,
1566                n as crate::ffi::R_xlen_t,
1567            );
1568            crate::ffi::Rf_protect(list);
1569            for (i, inner) in self.into_iter().enumerate() {
1570                let inner_sexp = inner.into_sexp_unchecked();
1571                list.set_vector_elt_unchecked(i as crate::ffi::R_xlen_t, inner_sexp);
1572            }
1573            crate::ffi::Rf_unprotect(1);
1574            list
1575        }
1576    }
1577}
1578
1579/// Convert `Vec<Vec<String>>` to R list of character vectors.
1580impl IntoR for Vec<Vec<String>> {
1581    type Error = std::convert::Infallible;
1582    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1583        Ok(self.into_sexp())
1584    }
1585    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1586        Ok(unsafe { self.into_sexp_unchecked() })
1587    }
1588    fn into_sexp(self) -> crate::ffi::SEXP {
1589        unsafe {
1590            let n = self.len();
1591            let list =
1592                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
1593            crate::ffi::Rf_protect(list);
1594
1595            for (i, inner) in self.into_iter().enumerate() {
1596                let inner_sexp = inner.into_sexp();
1597                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
1598            }
1599
1600            crate::ffi::Rf_unprotect(1);
1601            list
1602        }
1603    }
1604
1605    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1606        unsafe {
1607            let n = self.len();
1608            let list = crate::ffi::Rf_allocVector_unchecked(
1609                crate::ffi::SEXPTYPE::VECSXP,
1610                n as crate::ffi::R_xlen_t,
1611            );
1612            crate::ffi::Rf_protect(list);
1613
1614            for (i, inner) in self.into_iter().enumerate() {
1615                let inner_sexp = inner.into_sexp_unchecked();
1616                list.set_vector_elt_unchecked(i as crate::ffi::R_xlen_t, inner_sexp);
1617            }
1618
1619            crate::ffi::Rf_unprotect(1);
1620            list
1621        }
1622    }
1623}
1624// endregion
1625
1626// region: NA-aware vector conversions
1627
1628/// Macro for NA-aware `Vec<Option<T>> → R` vector conversions.
1629///
1630/// Uses `alloc_r_vector` to get a mutable slice, then fills it.
1631macro_rules! impl_vec_option_into_r {
1632    ($t:ty, $na_value:expr) => {
1633        impl IntoR for Vec<Option<$t>> {
1634            type Error = std::convert::Infallible;
1635            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1636                Ok(self.into_sexp())
1637            }
1638            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1639                Ok(unsafe { self.into_sexp_unchecked() })
1640            }
1641            fn into_sexp(self) -> crate::ffi::SEXP {
1642                unsafe {
1643                    let (sexp, dst) = alloc_r_vector::<$t>(self.len());
1644                    for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
1645                        *slot = val.unwrap_or($na_value);
1646                    }
1647                    sexp
1648                }
1649            }
1650
1651            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1652                unsafe {
1653                    let (sexp, dst) = alloc_r_vector_unchecked::<$t>(self.len());
1654                    for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
1655                        *slot = val.unwrap_or($na_value);
1656                    }
1657                    sexp
1658                }
1659            }
1660        }
1661    };
1662}
1663
1664impl_vec_option_into_r!(f64, NA_REAL); // NA_real_
1665impl_vec_option_into_r!(i32, NA_INTEGER); // NA_integer_
1666
1667/// Macro for NA-aware `Vec<Option<T>> → R` smart vector conversion.
1668/// Checks if all non-None values fit i32 → INTSXP, otherwise REALSXP.
1669///
1670/// Allocates the R vector directly and coerces in-place — no intermediate Vec.
1671macro_rules! impl_vec_option_smart_i64_into_r {
1672    ($t:ty, $fits_i32:expr) => {
1673        impl IntoR for Vec<Option<$t>> {
1674            type Error = std::convert::Infallible;
1675            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1676                Ok(self.into_sexp())
1677            }
1678            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1679                self.try_into_sexp()
1680            }
1681            fn into_sexp(self) -> crate::ffi::SEXP {
1682                unsafe {
1683                    if self.iter().all(|opt| match opt {
1684                        Some(x) => $fits_i32(*x),
1685                        None => true,
1686                    }) {
1687                        let (sexp, dst) = alloc_r_vector::<i32>(self.len());
1688                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
1689                            *slot = match val {
1690                                Some(x) => x as i32,
1691                                None => NA_INTEGER,
1692                            };
1693                        }
1694                        sexp
1695                    } else {
1696                        let (sexp, dst) = alloc_r_vector::<f64>(self.len());
1697                        for (slot, val) in dst.iter_mut().zip(self.into_iter()) {
1698                            *slot = match val {
1699                                Some(x) => x as f64,
1700                                None => NA_REAL,
1701                            };
1702                        }
1703                        sexp
1704                    }
1705                }
1706            }
1707        }
1708    };
1709}
1710
1711// i32::MIN is NA_integer_ in R, so exclude it
1712impl_vec_option_smart_i64_into_r!(i64, |x: i64| x > i32::MIN as i64 && x <= i32::MAX as i64);
1713impl_vec_option_smart_i64_into_r!(u64, |x: u64| x <= i32::MAX as u64);
1714impl_vec_option_smart_i64_into_r!(isize, |x: isize| x > i32::MIN as isize
1715    && x <= i32::MAX as isize);
1716impl_vec_option_smart_i64_into_r!(usize, |x: usize| x <= i32::MAX as usize);
1717
1718/// Macro for `Vec<Option<T>>` where `T` coerces to a type with existing Option impl.
1719///
1720/// Delegates to the target type's `Vec<Option<$to>>` impl (which itself uses alloc_r_vector).
1721macro_rules! impl_vec_option_coerce_into_r {
1722    ($from:ty => $to:ty) => {
1723        impl IntoR for Vec<Option<$from>> {
1724            type Error = std::convert::Infallible;
1725            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1726                Ok(self.into_sexp())
1727            }
1728            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1729                self.try_into_sexp()
1730            }
1731            fn into_sexp(self) -> crate::ffi::SEXP {
1732                // Delegate to the target Option type's impl (coerce inline)
1733                let coerced: Vec<Option<$to>> = self
1734                    .into_iter()
1735                    .map(|opt| opt.map(|x| <$to>::from(x)))
1736                    .collect();
1737                coerced.into_sexp()
1738            }
1739        }
1740    };
1741}
1742
1743impl_vec_option_coerce_into_r!(i8 => i32);
1744impl_vec_option_coerce_into_r!(i16 => i32);
1745impl_vec_option_coerce_into_r!(u16 => i32);
1746impl_vec_option_coerce_into_r!(u32 => i64); // delegates to smart i64 path
1747impl_vec_option_coerce_into_r!(f32 => f64);
1748
1749/// Helper: allocate LGLSXP and fill from an i32 iterator (checked).
1750///
1751/// Uses `alloc_r_vector` — logical vectors are `RLogical` (repr(transparent) i32).
1752fn logical_iter_to_lglsxp(n: usize, iter: impl Iterator<Item = i32>) -> crate::ffi::SEXP {
1753    unsafe {
1754        let (sexp, dst) = alloc_r_vector::<crate::ffi::RLogical>(n);
1755        // RLogical is repr(transparent) over i32, safe to write i32 values.
1756        let dst_i32: &mut [i32] = std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<i32>(), n);
1757        for (slot, val) in dst_i32.iter_mut().zip(iter) {
1758            *slot = val;
1759        }
1760        sexp
1761    }
1762}
1763
1764/// Helper: allocate LGLSXP and fill from an i32 iterator (unchecked).
1765unsafe fn logical_iter_to_lglsxp_unchecked(
1766    n: usize,
1767    iter: impl Iterator<Item = i32>,
1768) -> crate::ffi::SEXP {
1769    unsafe {
1770        let (sexp, dst) = alloc_r_vector_unchecked::<crate::ffi::RLogical>(n);
1771        let dst_i32: &mut [i32] = std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<i32>(), n);
1772        for (slot, val) in dst_i32.iter_mut().zip(iter) {
1773            *slot = val;
1774        }
1775        sexp
1776    }
1777}
1778
1779/// Convert `Vec<bool>` to R logical vector.
1780impl IntoR for Vec<bool> {
1781    type Error = std::convert::Infallible;
1782    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1783        Ok(self.into_sexp())
1784    }
1785    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1786        Ok(unsafe { self.into_sexp_unchecked() })
1787    }
1788    fn into_sexp(self) -> crate::ffi::SEXP {
1789        let n = self.len();
1790        logical_iter_to_lglsxp(n, self.into_iter().map(i32::from))
1791    }
1792
1793    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1794        let n = self.len();
1795        unsafe { logical_iter_to_lglsxp_unchecked(n, self.into_iter().map(i32::from)) }
1796    }
1797}
1798
1799/// Convert `Box<[bool]>` to R logical vector.
1800impl IntoR for Box<[bool]> {
1801    type Error = std::convert::Infallible;
1802    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1803        Ok(self.into_sexp())
1804    }
1805    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1806        Ok(unsafe { self.into_sexp_unchecked() })
1807    }
1808    fn into_sexp(self) -> crate::ffi::SEXP {
1809        let n = self.len();
1810        logical_iter_to_lglsxp(n, self.iter().map(|&v| i32::from(v)))
1811    }
1812    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1813        let n = self.len();
1814        unsafe { logical_iter_to_lglsxp_unchecked(n, self.iter().map(|&v| i32::from(v))) }
1815    }
1816}
1817
1818/// Convert `&[bool]` to R logical vector.
1819impl IntoR for &[bool] {
1820    type Error = std::convert::Infallible;
1821    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1822        Ok(self.into_sexp())
1823    }
1824    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1825        Ok(unsafe { self.into_sexp_unchecked() })
1826    }
1827    fn into_sexp(self) -> crate::ffi::SEXP {
1828        let n = self.len();
1829        logical_iter_to_lglsxp(n, self.iter().map(|&v| i32::from(v)))
1830    }
1831
1832    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1833        let n = self.len();
1834        unsafe { logical_iter_to_lglsxp_unchecked(n, self.iter().map(|&v| i32::from(v))) }
1835    }
1836}
1837
1838macro_rules! impl_vec_option_logical_into_r {
1839    ($(#[$meta:meta])* $t:ty, $convert:expr) => {
1840        $(#[$meta])*
1841        impl IntoR for Vec<Option<$t>> {
1842            type Error = std::convert::Infallible;
1843            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1844                Ok(self.into_sexp())
1845            }
1846            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1847                Ok(unsafe { self.into_sexp_unchecked() })
1848            }
1849            fn into_sexp(self) -> crate::ffi::SEXP {
1850                let n = self.len();
1851                logical_iter_to_lglsxp(n, self.into_iter().map($convert))
1852            }
1853
1854            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1855                let n = self.len();
1856                unsafe { logical_iter_to_lglsxp_unchecked(n, self.into_iter().map($convert)) }
1857            }
1858        }
1859    };
1860}
1861
1862impl_vec_option_logical_into_r!(
1863    /// Convert `Vec<Option<bool>>` to R logical vector with NA support.
1864    bool,
1865    |v: Option<bool>| match v {
1866        Some(true) => 1,
1867        Some(false) => 0,
1868        None => NA_LOGICAL,
1869    }
1870);
1871impl_vec_option_logical_into_r!(
1872    /// Convert `Vec<Option<Rboolean>>` to R logical vector with NA support.
1873    crate::ffi::Rboolean,
1874    |v: Option<crate::ffi::Rboolean>| match v {
1875        Some(b) => b as i32,
1876        None => NA_LOGICAL,
1877    }
1878);
1879impl_vec_option_logical_into_r!(
1880    /// Convert `Vec<Option<RLogical>>` to R logical vector with NA support.
1881    crate::ffi::RLogical,
1882    |v: Option<crate::ffi::RLogical>| match v {
1883        Some(b) => b.to_i32(),
1884        None => NA_LOGICAL,
1885    }
1886);
1887
1888/// Convert `Vec<Option<String>>` to R character vector with NA support.
1889///
1890/// `None` values become `NA_character_` in R.
1891impl IntoR for Vec<Option<String>> {
1892    type Error = std::convert::Infallible;
1893    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1894        Ok(self.into_sexp())
1895    }
1896    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1897        Ok(unsafe { self.into_sexp_unchecked() })
1898    }
1899    fn into_sexp(self) -> crate::ffi::SEXP {
1900        unsafe {
1901            let n: crate::ffi::R_xlen_t = self
1902                .len()
1903                .try_into()
1904                .expect("vec length exceeds isize::MAX");
1905            let sexp =
1906                OwnedProtect::new(crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::STRSXP, n));
1907
1908            for (i, opt_s) in self.iter().enumerate() {
1909                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1910                let charsxp = match opt_s {
1911                    Some(s) => str_to_charsxp(s),
1912                    None => crate::ffi::SEXP::na_string(),
1913                };
1914                sexp.set_string_elt(idx, charsxp);
1915            }
1916
1917            *sexp
1918        }
1919    }
1920
1921    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1922        unsafe {
1923            let n: crate::ffi::R_xlen_t = self
1924                .len()
1925                .try_into()
1926                .expect("vec length exceeds isize::MAX");
1927            let sexp = OwnedProtect::new(crate::ffi::Rf_allocVector_unchecked(
1928                crate::ffi::SEXPTYPE::STRSXP,
1929                n,
1930            ));
1931
1932            for (i, opt_s) in self.iter().enumerate() {
1933                let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
1934                let charsxp = match opt_s {
1935                    Some(s) => str_to_charsxp_unchecked(s),
1936                    None => crate::ffi::SEXP::na_string(),
1937                };
1938                sexp.set_string_elt_unchecked(idx, charsxp);
1939            }
1940
1941            *sexp
1942        }
1943    }
1944}
1945// endregion
1946
1947// region: Tuple to list conversions
1948
1949/// Macro to implement IntoR for tuples of various sizes.
1950/// Converts Rust tuples to unnamed R lists (VECSXP).
1951macro_rules! impl_tuple_into_r {
1952    // Base case: 2-tuple
1953    (($($T:ident),+), ($($idx:tt),+), $n:expr) => {
1954        impl<$($T: IntoR),+> IntoR for ($($T,)+) {
1955            type Error = std::convert::Infallible;
1956            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
1957                Ok(self.into_sexp())
1958            }
1959            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
1960                Ok(unsafe { self.into_sexp_unchecked() })
1961            }
1962            fn into_sexp(self) -> crate::ffi::SEXP {
1963                unsafe {
1964                    let list = crate::ffi::Rf_allocVector(
1965                        crate::ffi::SEXPTYPE::VECSXP,
1966                        $n as crate::ffi::R_xlen_t
1967                    );
1968                    crate::ffi::Rf_protect(list);
1969
1970                    $(
1971
1972                            list.set_vector_elt($idx as crate::ffi::R_xlen_t, self.$idx.into_sexp()
1973                        );
1974                    )+
1975
1976                    crate::ffi::Rf_unprotect(1);
1977                    list
1978                }
1979            }
1980
1981            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
1982                unsafe {
1983                    let list = crate::ffi::Rf_allocVector_unchecked(
1984                        crate::ffi::SEXPTYPE::VECSXP,
1985                        $n as crate::ffi::R_xlen_t
1986                    );
1987                    crate::ffi::Rf_protect(list);
1988
1989                    $(
1990
1991                            list.set_vector_elt_unchecked($idx as crate::ffi::R_xlen_t, self.$idx.into_sexp_unchecked()
1992                        );
1993                    )+
1994
1995                    crate::ffi::Rf_unprotect(1);
1996                    list
1997                }
1998            }
1999        }
2000    };
2001}
2002
2003// Implement for tuples of sizes 2-8
2004impl_tuple_into_r!((A, B), (0, 1), 2);
2005impl_tuple_into_r!((A, B, C), (0, 1, 2), 3);
2006impl_tuple_into_r!((A, B, C, D), (0, 1, 2, 3), 4);
2007impl_tuple_into_r!((A, B, C, D, E), (0, 1, 2, 3, 4), 5);
2008impl_tuple_into_r!((A, B, C, D, E, F), (0, 1, 2, 3, 4, 5), 6);
2009impl_tuple_into_r!((A, B, C, D, E, F, G), (0, 1, 2, 3, 4, 5, 6), 7);
2010impl_tuple_into_r!((A, B, C, D, E, F, G, H), (0, 1, 2, 3, 4, 5, 6, 7), 8);
2011// endregion
2012
2013// region: ALTREP zero-copy extension trait
2014
2015/// Extension trait for ALTREP conversions.
2016///
2017/// This trait provides ergonomic methods for converting Rust types to R ALTREP
2018/// vectors without copying data. The data stays in Rust memory (wrapped in an
2019/// ExternalPtr) and R accesses it via ALTREP callbacks.
2020///
2021/// # Performance Characteristics
2022///
2023/// | Operation | Regular (IntoR) | ALTREP (IntoRAltrep) |
2024/// |-----------|-----------------|------------------------|
2025/// | Creation | O(n) copy | O(1) wrap |
2026/// | Memory | Duplicated in R | Single copy in Rust |
2027/// | Element access | Direct pointer | Callback (~10ns overhead) |
2028/// | DATAPTR ops | O(1) | O(1) if Vec/Box, N/A if lazy |
2029///
2030/// # When to Use ALTREP
2031///
2032/// **Good candidates**:
2033/// - ✅ Large vectors (>1000 elements)
2034/// - ✅ Lazy/computed data (avoid eager materialization)
2035/// - ✅ External data sources (files, databases, APIs)
2036/// - ✅ Data that might not be fully accessed by R
2037///
2038/// **Not recommended**:
2039/// - ❌ Small vectors (<100 elements) - copy overhead is negligible
2040/// - ❌ Data R will immediately modify (triggers copy anyway)
2041/// - ❌ Temporary results (extra indirection not worth it)
2042///
2043/// # Example
2044///
2045/// ```rust,ignore
2046/// use miniextendr_api::{miniextendr, IntoRAltrep, IntoR, ffi::SEXP};
2047///
2048/// #[miniextendr]
2049/// fn large_dataset() -> SEXP {
2050///     let data: Vec<f64> = (0..1_000_000).map(|i| i as f64).collect();
2051///
2052///     // Zero-copy: wraps pointer instead of copying 1M elements
2053///     data.into_sexp_altrep()
2054/// }
2055///
2056/// #[miniextendr]
2057/// fn small_result() -> SEXP {
2058///     let data = vec![1, 2, 3, 4, 5];
2059///
2060///     // Regular copy is fine for small data
2061///     data.into_sexp()
2062/// }
2063/// ```
2064pub trait IntoRAltrep {
2065    /// Convert to R SEXP using ALTREP zero-copy representation.
2066    ///
2067    /// This is equivalent to `Altrep(self).into_sexp()` but more discoverable
2068    /// and explicit about the zero-copy intent.
2069    fn into_sexp_altrep(self) -> crate::ffi::SEXP;
2070
2071    /// Convert to R SEXP using ALTREP, skipping debug thread assertions.
2072    ///
2073    /// # Safety
2074    ///
2075    /// Caller must ensure they are on R's main thread.
2076    unsafe fn into_sexp_altrep_unchecked(self) -> crate::ffi::SEXP
2077    where
2078        Self: Sized,
2079    {
2080        self.into_sexp_altrep()
2081    }
2082
2083    /// Create an `Altrep<Self>` wrapper.
2084    ///
2085    /// This returns the wrapper explicitly, allowing you to store it or
2086    /// further process it before conversion.
2087    fn into_altrep(self) -> Altrep<Self>
2088    where
2089        Self: Sized,
2090    {
2091        Altrep(self)
2092    }
2093}
2094
2095impl<T> IntoRAltrep for T
2096where
2097    T: crate::altrep::RegisterAltrep + crate::externalptr::TypedExternal,
2098{
2099    fn into_sexp_altrep(self) -> crate::ffi::SEXP {
2100        Altrep(self).into_sexp()
2101    }
2102
2103    unsafe fn into_sexp_altrep_unchecked(self) -> crate::ffi::SEXP {
2104        unsafe { Altrep(self).into_sexp_unchecked() }
2105    }
2106}
2107// endregion
2108
2109// region: Additional collection type conversions for DataFrameRow support
2110
2111/// Convert `Vec<Box<[T]>>` to R list of vectors (for RNativeType elements).
2112/// Each boxed slice becomes an R vector.
2113impl<T> IntoR for Vec<Box<[T]>>
2114where
2115    T: crate::ffi::RNativeType,
2116{
2117    type Error = std::convert::Infallible;
2118    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2119        Ok(self.into_sexp())
2120    }
2121    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2122        self.try_into_sexp()
2123    }
2124    fn into_sexp(self) -> crate::ffi::SEXP {
2125        unsafe {
2126            let n = self.len();
2127            let list =
2128                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
2129            crate::ffi::Rf_protect(list);
2130
2131            for (i, boxed_slice) in self.into_iter().enumerate() {
2132                let vec: Vec<T> = boxed_slice.into_vec();
2133                let inner_sexp = vec.into_sexp();
2134                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
2135            }
2136
2137            crate::ffi::Rf_unprotect(1);
2138            list
2139        }
2140    }
2141}
2142
2143/// Convert `Vec<Box<[String]>>` to R list of character vectors.
2144impl IntoR for Vec<Box<[String]>> {
2145    type Error = std::convert::Infallible;
2146    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2147        Ok(self.into_sexp())
2148    }
2149    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2150        self.try_into_sexp()
2151    }
2152    fn into_sexp(self) -> crate::ffi::SEXP {
2153        unsafe {
2154            let n = self.len();
2155            let list =
2156                crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
2157            crate::ffi::Rf_protect(list);
2158
2159            for (i, boxed_slice) in self.into_iter().enumerate() {
2160                let vec: Vec<String> = boxed_slice.into_vec();
2161                let inner_sexp = vec.into_sexp();
2162                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
2163            }
2164
2165            crate::ffi::Rf_unprotect(1);
2166            list
2167        }
2168    }
2169}
2170
2171/// Convert `Vec<[T; N]>` to R list of vectors.
2172/// Each array becomes an R vector.
2173impl<T, const N: usize> IntoR for Vec<[T; N]>
2174where
2175    T: crate::ffi::RNativeType,
2176{
2177    type Error = std::convert::Infallible;
2178    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2179        Ok(self.into_sexp())
2180    }
2181    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2182        self.try_into_sexp()
2183    }
2184    fn into_sexp(self) -> crate::ffi::SEXP {
2185        unsafe {
2186            let len = self.len();
2187            let list = crate::ffi::Rf_allocVector(
2188                crate::ffi::SEXPTYPE::VECSXP,
2189                len as crate::ffi::R_xlen_t,
2190            );
2191            crate::ffi::Rf_protect(list);
2192
2193            for (i, array) in self.into_iter().enumerate() {
2194                let vec: Vec<T> = array.into();
2195                let inner_sexp = vec.into_sexp();
2196                list.set_vector_elt(i as crate::ffi::R_xlen_t, inner_sexp);
2197            }
2198
2199            crate::ffi::Rf_unprotect(1);
2200            list
2201        }
2202    }
2203}
2204
2205/// Helper: convert a Vec of IntoR items to an R list (VECSXP).
2206fn vec_of_into_r_to_list<T: IntoR>(items: Vec<T>) -> crate::ffi::SEXP {
2207    unsafe {
2208        let n = items.len();
2209        let list = OwnedProtect::new(crate::ffi::Rf_allocVector(
2210            crate::ffi::SEXPTYPE::VECSXP,
2211            n as crate::ffi::R_xlen_t,
2212        ));
2213        for (i, item) in items.into_iter().enumerate() {
2214            list.get()
2215                .set_vector_elt(i as crate::ffi::R_xlen_t, item.into_sexp());
2216        }
2217        *list
2218    }
2219}
2220
2221// region: Vec<Option<Collection>> conversions ──────────────────────────────────
2222
2223/// Helper: convert `Vec<Option<C: IntoR>>` to a VECSXP, with `None` mapping to
2224/// `R_NilValue` (NULL) and `Some(v)` mapping to whatever `v.into_sexp()` produces.
2225fn vec_option_of_into_r_to_list<T: IntoR>(items: Vec<Option<T>>) -> crate::ffi::SEXP {
2226    unsafe {
2227        let n = items.len();
2228        let list = OwnedProtect::new(crate::ffi::Rf_allocVector(
2229            crate::ffi::SEXPTYPE::VECSXP,
2230            n as crate::ffi::R_xlen_t,
2231        ));
2232        for (i, item) in items.into_iter().enumerate() {
2233            let elt = match item {
2234                Some(v) => v.into_sexp(),
2235                None => crate::ffi::SEXP::nil(),
2236            };
2237            list.get().set_vector_elt(i as crate::ffi::R_xlen_t, elt);
2238        }
2239        *list
2240    }
2241}
2242
2243/// Convert `Vec<Option<Vec<T>>>` to R list where `None` → NULL, `Some(v)` → typed vector.
2244impl<T: crate::ffi::RNativeType> IntoR for Vec<Option<Vec<T>>>
2245where
2246    Vec<T>: IntoR,
2247{
2248    type Error = std::convert::Infallible;
2249    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2250        Ok(self.into_sexp())
2251    }
2252    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2253        self.try_into_sexp()
2254    }
2255    fn into_sexp(self) -> crate::ffi::SEXP {
2256        vec_option_of_into_r_to_list(self)
2257    }
2258}
2259
2260/// Convert `Vec<Option<Vec<String>>>` to R list where `None` → NULL, `Some(v)` → character vector.
2261impl IntoR for Vec<Option<Vec<String>>> {
2262    type Error = std::convert::Infallible;
2263    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2264        Ok(self.into_sexp())
2265    }
2266    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2267        self.try_into_sexp()
2268    }
2269    fn into_sexp(self) -> crate::ffi::SEXP {
2270        vec_option_of_into_r_to_list(self)
2271    }
2272}
2273
2274/// Convert `Vec<Option<HashSet<T>>>` to R list where `None` → NULL, `Some(s)` → unordered vector.
2275impl<T: crate::ffi::RNativeType + Eq + Hash> IntoR for Vec<Option<HashSet<T>>>
2276where
2277    HashSet<T>: IntoR,
2278{
2279    type Error = std::convert::Infallible;
2280    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2281        Ok(self.into_sexp())
2282    }
2283    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2284        self.try_into_sexp()
2285    }
2286    fn into_sexp(self) -> crate::ffi::SEXP {
2287        vec_option_of_into_r_to_list(self)
2288    }
2289}
2290
2291/// Convert `Vec<Option<HashSet<String>>>` to R list where `None` → NULL, `Some(s)` → character vector.
2292impl IntoR for Vec<Option<HashSet<String>>> {
2293    type Error = std::convert::Infallible;
2294    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2295        Ok(self.into_sexp())
2296    }
2297    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2298        self.try_into_sexp()
2299    }
2300    fn into_sexp(self) -> crate::ffi::SEXP {
2301        vec_option_of_into_r_to_list(self)
2302    }
2303}
2304
2305/// Convert `Vec<Option<BTreeSet<T>>>` to R list where `None` → NULL, `Some(s)` → sorted vector.
2306impl<T: crate::ffi::RNativeType + Ord> IntoR for Vec<Option<BTreeSet<T>>>
2307where
2308    BTreeSet<T>: IntoR,
2309{
2310    type Error = std::convert::Infallible;
2311    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2312        Ok(self.into_sexp())
2313    }
2314    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2315        self.try_into_sexp()
2316    }
2317    fn into_sexp(self) -> crate::ffi::SEXP {
2318        vec_option_of_into_r_to_list(self)
2319    }
2320}
2321
2322/// Convert `Vec<Option<BTreeSet<String>>>` to R list where `None` → NULL, `Some(s)` → sorted character vector.
2323impl IntoR for Vec<Option<BTreeSet<String>>> {
2324    type Error = std::convert::Infallible;
2325    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2326        Ok(self.into_sexp())
2327    }
2328    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2329        self.try_into_sexp()
2330    }
2331    fn into_sexp(self) -> crate::ffi::SEXP {
2332        vec_option_of_into_r_to_list(self)
2333    }
2334}
2335
2336/// Convert `Vec<Option<HashMap<String, V>>>` to R list where `None` → NULL, `Some(m)` → named list.
2337impl<V: IntoR> IntoR for Vec<Option<HashMap<String, V>>> {
2338    type Error = std::convert::Infallible;
2339    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2340        Ok(self.into_sexp())
2341    }
2342    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2343        self.try_into_sexp()
2344    }
2345    fn into_sexp(self) -> crate::ffi::SEXP {
2346        vec_option_of_into_r_to_list(self)
2347    }
2348}
2349
2350/// Convert `Vec<Option<BTreeMap<String, V>>>` to R list where `None` → NULL, `Some(m)` → named list.
2351impl<V: IntoR> IntoR for Vec<Option<BTreeMap<String, V>>> {
2352    type Error = std::convert::Infallible;
2353    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2354        Ok(self.into_sexp())
2355    }
2356    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2357        self.try_into_sexp()
2358    }
2359    fn into_sexp(self) -> crate::ffi::SEXP {
2360        vec_option_of_into_r_to_list(self)
2361    }
2362}
2363
2364/// Convert `Vec<Option<&[T]>>` to R list where `None` → NULL, `Some(s)` → typed vector.
2365///
2366/// Borrowed analogue of `Vec<Option<Vec<T>>>` — each slice is copied into a fresh R vector.
2367impl<T: crate::ffi::RNativeType> IntoR for Vec<Option<&[T]>> {
2368    type Error = std::convert::Infallible;
2369    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2370        Ok(self.into_sexp())
2371    }
2372    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2373        self.try_into_sexp()
2374    }
2375    fn into_sexp(self) -> crate::ffi::SEXP {
2376        vec_option_of_into_r_to_list(self)
2377    }
2378}
2379
2380/// Convert `Vec<Option<&[String]>>` to R list where `None` → NULL, `Some(s)` → character vector.
2381///
2382/// Borrowed analogue of `Vec<Option<Vec<String>>>`.
2383impl IntoR for Vec<Option<&[String]>> {
2384    type Error = std::convert::Infallible;
2385    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2386        Ok(self.into_sexp())
2387    }
2388    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2389        self.try_into_sexp()
2390    }
2391    fn into_sexp(self) -> crate::ffi::SEXP {
2392        vec_option_of_into_r_to_list(self)
2393    }
2394}
2395
2396// endregion
2397
2398/// Convert `Vec<HashSet<T>>` to R list of vectors (for RNativeType elements).
2399/// Each HashSet becomes an R vector (unordered).
2400impl<T: crate::ffi::RNativeType> IntoR for Vec<std::collections::HashSet<T>>
2401where
2402    Vec<T>: IntoR,
2403{
2404    type Error = std::convert::Infallible;
2405    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2406        Ok(self.into_sexp())
2407    }
2408    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2409        self.try_into_sexp()
2410    }
2411    fn into_sexp(self) -> crate::ffi::SEXP {
2412        let converted: Vec<Vec<T>> = self.into_iter().map(|s| s.into_iter().collect()).collect();
2413        vec_of_into_r_to_list(converted)
2414    }
2415}
2416
2417/// Convert `Vec<BTreeSet<T>>` to R list of vectors (for RNativeType elements).
2418/// Each BTreeSet becomes an R vector (sorted).
2419impl<T: crate::ffi::RNativeType> IntoR for Vec<std::collections::BTreeSet<T>>
2420where
2421    Vec<T>: IntoR,
2422{
2423    type Error = std::convert::Infallible;
2424    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2425        Ok(self.into_sexp())
2426    }
2427    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2428        self.try_into_sexp()
2429    }
2430    fn into_sexp(self) -> crate::ffi::SEXP {
2431        let converted: Vec<Vec<T>> = self.into_iter().map(|s| s.into_iter().collect()).collect();
2432        vec_of_into_r_to_list(converted)
2433    }
2434}
2435
2436/// Convert `Vec<HashSet<String>>` to R list of character vectors.
2437impl IntoR for Vec<std::collections::HashSet<String>> {
2438    type Error = std::convert::Infallible;
2439    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2440        Ok(self.into_sexp())
2441    }
2442    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2443        self.try_into_sexp()
2444    }
2445    fn into_sexp(self) -> crate::ffi::SEXP {
2446        let converted: Vec<Vec<String>> =
2447            self.into_iter().map(|s| s.into_iter().collect()).collect();
2448        vec_of_into_r_to_list(converted)
2449    }
2450}
2451
2452/// Convert `Vec<BTreeSet<String>>` to R list of character vectors.
2453impl IntoR for Vec<std::collections::BTreeSet<String>> {
2454    type Error = std::convert::Infallible;
2455    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2456        Ok(self.into_sexp())
2457    }
2458    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2459        self.try_into_sexp()
2460    }
2461    fn into_sexp(self) -> crate::ffi::SEXP {
2462        let converted: Vec<Vec<String>> =
2463            self.into_iter().map(|s| s.into_iter().collect()).collect();
2464        vec_of_into_r_to_list(converted)
2465    }
2466}
2467
2468macro_rules! impl_vec_map_into_r {
2469    ($(#[$meta:meta])* $map_ty:ident) => {
2470        $(#[$meta])*
2471        impl<V: IntoR> IntoR for Vec<$map_ty<String, V>> {
2472            type Error = std::convert::Infallible;
2473            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
2474                Ok(self.into_sexp())
2475            }
2476            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
2477                self.try_into_sexp()
2478            }
2479            fn into_sexp(self) -> crate::ffi::SEXP {
2480                vec_of_maps_to_list(self)
2481            }
2482        }
2483    };
2484}
2485
2486impl_vec_map_into_r!(
2487    /// Convert `Vec<HashMap<String, V>>` to R list of named lists.
2488    HashMap
2489);
2490impl_vec_map_into_r!(
2491    /// Convert `Vec<BTreeMap<String, V>>` to R list of named lists.
2492    BTreeMap
2493);
2494
2495/// Helper to convert a Vec of map-like types to an R list of named lists.
2496fn vec_of_maps_to_list<T: IntoR>(vec: Vec<T>) -> crate::ffi::SEXP {
2497    unsafe {
2498        let n = vec.len();
2499        let list =
2500            crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n as crate::ffi::R_xlen_t);
2501        crate::ffi::Rf_protect(list);
2502
2503        for (i, map) in vec.into_iter().enumerate() {
2504            list.set_vector_elt(i as crate::ffi::R_xlen_t, map.into_sexp());
2505        }
2506
2507        crate::ffi::Rf_unprotect(1);
2508        list
2509    }
2510}
2511// endregion
2512
2513// region: R connections — IntoR impls (issue #175, #176)
2514
2515#[cfg(feature = "connections")]
2516mod connections_into_r {
2517    use crate::connection::{RNullConnection, RStderr, RStdin, RStdout};
2518    use crate::ffi::SEXP;
2519    use crate::into_r::IntoR;
2520
2521    // Evaluate a no-arg base function and return the resulting SEXP (unprotected).
2522    //
2523    // # Safety
2524    // Must be called from the R main thread.
2525    unsafe fn eval_base_noarg(name: &std::ffi::CStr) -> SEXP {
2526        use crate::ffi::{R_BaseEnv, Rf_install, Rf_lang1, Rf_protect, Rf_unprotect};
2527        unsafe {
2528            let call = Rf_lang1(Rf_install(name.as_ptr()));
2529            Rf_protect(call);
2530            let mut err: std::os::raw::c_int = 0;
2531            let result = crate::ffi::R_tryEvalSilent(call, R_BaseEnv, &mut err);
2532            Rf_unprotect(1);
2533            if err != 0 {
2534                panic!("failed to evaluate {}()", name.to_string_lossy());
2535            }
2536            result
2537        }
2538    }
2539
2540    impl IntoR for RStdin {
2541        type Error = std::convert::Infallible;
2542
2543        fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
2544            Ok(self.into_sexp())
2545        }
2546
2547        fn into_sexp(self) -> SEXP {
2548            unsafe { eval_base_noarg(c"stdin") }
2549        }
2550    }
2551
2552    impl IntoR for RStdout {
2553        type Error = std::convert::Infallible;
2554
2555        fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
2556            Ok(self.into_sexp())
2557        }
2558
2559        fn into_sexp(self) -> SEXP {
2560            unsafe { eval_base_noarg(c"stdout") }
2561        }
2562    }
2563
2564    impl IntoR for RStderr {
2565        type Error = std::convert::Infallible;
2566
2567        fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
2568            Ok(self.into_sexp())
2569        }
2570
2571        fn into_sexp(self) -> SEXP {
2572            unsafe { eval_base_noarg(c"stderr") }
2573        }
2574    }
2575
2576    /// Returns the held SEXP and disarms the `Drop` guard (no double-close).
2577    impl IntoR for RNullConnection {
2578        type Error = std::convert::Infallible;
2579
2580        fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
2581            Ok(self.into_sexp())
2582        }
2583
2584        fn into_sexp(self) -> SEXP {
2585            let sexp = self.sexp();
2586            // Transfer ownership to R: release from precious list (R's connection
2587            // table keeps the connection alive), then forget self to skip Drop.
2588            unsafe { crate::ffi::R_ReleaseObject(sexp) };
2589            std::mem::forget(self);
2590            sexp
2591        }
2592    }
2593}
2594
2595// endregion
2596
2597// region: txtProgressBar — IntoR (issue #177)
2598
2599#[cfg(feature = "connections")]
2600mod txt_progress_bar_into_r {
2601    use crate::ffi::SEXP;
2602    use crate::into_r::IntoR;
2603    use crate::txt_progress_bar::RTxtProgressBar;
2604
2605    /// Transfer ownership of the `RTxtProgressBar` back to R.
2606    ///
2607    /// Releases the SEXP from the precious list and forgets `self` (so `Drop`
2608    /// is a no-op). R's environment keeps the bar alive after this call.
2609    impl IntoR for RTxtProgressBar {
2610        type Error = std::convert::Infallible;
2611
2612        fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
2613            Ok(self.into_sexp())
2614        }
2615
2616        fn into_sexp(self) -> SEXP {
2617            let sexp = self.sexp();
2618            // Release from precious list; R manages lifetime from here on.
2619            unsafe { crate::ffi::R_ReleaseObject(sexp) };
2620            std::mem::forget(self); // Disarm Drop guard — no double-release.
2621            sexp
2622        }
2623    }
2624}
2625
2626// endregion