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