Skip to main content

miniextendr_api/
into_r_as.rs

1//! Storage-directed conversion to R.
2//!
3//! This module provides [`IntoRAs`], a trait for converting Rust values to R SEXPs
4//! with explicit target storage type selection.
5//!
6//! # Value-Based Semantics
7//!
8//! Conversions are **runtime-checked**: if the actual value fits the target type,
9//! it converts; if not, it errors. There is no "lossy" escape hatch - if you want
10//! lossy conversion, cast the values yourself first.
11//!
12//! # Where this fits among the three outbound paths
13//!
14//! - [`crate::into_r::IntoR`] — **lax** default: picks an R storage type for
15//!   you (e.g. `i64` → `INTSXP` if it fits, `REALSXP` otherwise). Silent.
16//! - [`crate::strict`] — **strict** opt-in via `#[miniextendr(strict)]`:
17//!   panics (→ R error) if the value can't fit `INTSXP`.
18//! - `IntoRAs` (this module) — **storage-directed**: the *caller* picks the
19//!   target R type, the conversion errors with [`StorageCoerceError`] if any
20//!   value doesn't fit.
21//!
22//! Failure mode of reaching for the default `IntoR` when you actually wanted
23//! storage control: an R-side `is.integer()` check fails because R received
24//! a `REALSXP` your `tibble` column wasn't expecting.
25//!
26//! # Example
27//!
28//! ```ignore
29//! use miniextendr_api::IntoRAs;
30//!
31//! // These succeed (values fit)
32//! let x = vec![1_i64, 2, 3];
33//! let sexp = x.into_r_as::<i32>()?;           // OK: all values in i32 range
34//!
35//! let y = vec![1.0_f64, 2.0, 3.0];
36//! let sexp = y.into_r_as::<i32>()?;           // OK: all values are integral
37//!
38//! // These fail (values don't fit)
39//! let z = vec![1_i64 << 40];
40//! let sexp = z.into_r_as::<i32>()?;           // Error: out of range
41//!
42//! let w = vec![1.5_f64];
43//! let sexp = w.into_r_as::<i32>()?;           // Error: not integral
44//!
45//! // User wants lossy? Cast first.
46//! let lossy: Vec<i32> = vec![1.5_f64, 2.7].iter().map(|&x| x as i32).collect();
47//! let sexp = lossy.into_r();                  // [1, 2] - user's responsibility
48//! ```
49
50use crate::coerce::{CoerceError, TryCoerce};
51use crate::from_r::BATCHED_ERROR_CAP;
52use crate::into_r::IntoR;
53use crate::{IsNA, RLogical, SEXP};
54use std::fmt;
55
56// region: Error type
57
58/// Error type for storage-directed conversion failures.
59#[derive(#[automatically_derived]
impl ::core::fmt::Debug for StorageCoerceError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Unsupported { from: __self_0, to: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "Unsupported", "from", __self_0, "to", &__self_1),
            Self::OutOfRange { from: __self_0, to: __self_1, index: __self_2 }
                =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f,
                    "OutOfRange", "from", __self_0, "to", __self_1, "index",
                    &__self_2),
            Self::NonFinite { to: __self_0, index: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "NonFinite", "to", __self_0, "index", &__self_1),
            Self::PrecisionLoss { to: __self_0, index: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "PrecisionLoss", "to", __self_0, "index", &__self_1),
            Self::NotIntegral { to: __self_0, index: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "NotIntegral", "to", __self_0, "index", &__self_1),
            Self::MissingValue { to: __self_0, index: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "MissingValue", "to", __self_0, "index", &__self_1),
            Self::InvalidUtf8 { index: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "InvalidUtf8", "index", &__self_0),
            Self::Batched {
                container: __self_0, listed: __self_1, total: __self_2 } =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f,
                    "Batched", "container", __self_0, "listed", __self_1,
                    "total", &__self_2),
        }
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for StorageCoerceError {
    #[inline]
    fn clone(&self) -> Self {
        match self {
            Self::Unsupported { from: __self_0, to: __self_1 } =>
                StorageCoerceError::Unsupported {
                    from: ::core::clone::Clone::clone(__self_0),
                    to: ::core::clone::Clone::clone(__self_1),
                },
            Self::OutOfRange { from: __self_0, to: __self_1, index: __self_2 }
                =>
                StorageCoerceError::OutOfRange {
                    from: ::core::clone::Clone::clone(__self_0),
                    to: ::core::clone::Clone::clone(__self_1),
                    index: ::core::clone::Clone::clone(__self_2),
                },
            Self::NonFinite { to: __self_0, index: __self_1 } =>
                StorageCoerceError::NonFinite {
                    to: ::core::clone::Clone::clone(__self_0),
                    index: ::core::clone::Clone::clone(__self_1),
                },
            Self::PrecisionLoss { to: __self_0, index: __self_1 } =>
                StorageCoerceError::PrecisionLoss {
                    to: ::core::clone::Clone::clone(__self_0),
                    index: ::core::clone::Clone::clone(__self_1),
                },
            Self::NotIntegral { to: __self_0, index: __self_1 } =>
                StorageCoerceError::NotIntegral {
                    to: ::core::clone::Clone::clone(__self_0),
                    index: ::core::clone::Clone::clone(__self_1),
                },
            Self::MissingValue { to: __self_0, index: __self_1 } =>
                StorageCoerceError::MissingValue {
                    to: ::core::clone::Clone::clone(__self_0),
                    index: ::core::clone::Clone::clone(__self_1),
                },
            Self::InvalidUtf8 { index: __self_0 } =>
                StorageCoerceError::InvalidUtf8 {
                    index: ::core::clone::Clone::clone(__self_0),
                },
            Self::Batched {
                container: __self_0, listed: __self_1, total: __self_2 } =>
                StorageCoerceError::Batched {
                    container: ::core::clone::Clone::clone(__self_0),
                    listed: ::core::clone::Clone::clone(__self_1),
                    total: ::core::clone::Clone::clone(__self_2),
                },
        }
    }
}Clone, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for StorageCoerceError { }
#[automatically_derived]
impl ::core::cmp::PartialEq for StorageCoerceError {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::Unsupported { from: __self_0, to: __self_1 },
                    Self::Unsupported { from: __arg1_0, to: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (Self::OutOfRange {
                    from: __self_0, to: __self_1, index: __self_2 },
                    Self::OutOfRange {
                    from: __arg1_0, to: __arg1_1, index: __arg1_2 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1 &&
                        __self_2 == __arg1_2,
                (Self::NonFinite { to: __self_0, index: __self_1 },
                    Self::NonFinite { to: __arg1_0, index: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (Self::PrecisionLoss { to: __self_0, index: __self_1 },
                    Self::PrecisionLoss { to: __arg1_0, index: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (Self::NotIntegral { to: __self_0, index: __self_1 },
                    Self::NotIntegral { to: __arg1_0, index: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (Self::MissingValue { to: __self_0, index: __self_1 },
                    Self::MissingValue { to: __arg1_0, index: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (Self::InvalidUtf8 { index: __self_0 }, Self::InvalidUtf8 {
                    index: __arg1_0 }) => __self_0 == __arg1_0,
                (Self::Batched {
                    container: __self_0, listed: __self_1, total: __self_2 },
                    Self::Batched {
                    container: __arg1_0, listed: __arg1_1, total: __arg1_2 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1 &&
                        __self_2 == __arg1_2,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for StorageCoerceError {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<Option<usize>>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<Option<usize>>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<Option<usize>>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<Option<usize>>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<Option<usize>>;
        let _: ::core::cmp::AssertParamIsEq<Option<usize>>;
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
        let _: ::core::cmp::AssertParamIsEq<Vec<StorageCoerceError>>;
        let _: ::core::cmp::AssertParamIsEq<usize>;
    }
}Eq)]
60pub enum StorageCoerceError {
61    /// Conversion between these types is not supported.
62    Unsupported {
63        /// Source Rust type name.
64        from: &'static str,
65        /// Target storage type name.
66        to: &'static str,
67    },
68    /// Value is out of range for the target type.
69    OutOfRange {
70        /// Source Rust type name.
71        from: &'static str,
72        /// Target storage type name.
73        to: &'static str,
74        /// Failing element index (0-based) for vector conversions.
75        index: Option<usize>,
76    },
77    /// Value is non-finite (NaN or Inf) but target requires finite.
78    NonFinite {
79        /// Target storage type name.
80        to: &'static str,
81        /// Failing element index (0-based) for vector conversions.
82        index: Option<usize>,
83    },
84    /// Conversion would lose precision.
85    PrecisionLoss {
86        /// Target storage type name.
87        to: &'static str,
88        /// Failing element index (0-based) for vector conversions.
89        index: Option<usize>,
90    },
91    /// Float value is not integral but target is integer type.
92    NotIntegral {
93        /// Target storage type name.
94        to: &'static str,
95        /// Failing element index (0-based) for vector conversions.
96        index: Option<usize>,
97    },
98    /// Missing value (NA) cannot be represented in target type.
99    MissingValue {
100        /// Target storage type name.
101        to: &'static str,
102        /// Failing element index (0-based) for vector conversions.
103        index: Option<usize>,
104    },
105    /// Invalid UTF-8 in string conversion.
106    InvalidUtf8 {
107        /// Failing element index (0-based) for vector conversions.
108        index: Option<usize>,
109    },
110    /// Aggregated per-element failures from a vector conversion (#1097).
111    /// `listed` holds at most the first `BATCHED_ERROR_CAP` element errors
112    /// (each with `index` set); `total` counts all failures.
113    Batched {
114        /// The vector/slice type that failed to convert, e.g. `"Vec<i64>"`.
115        container: &'static str,
116        /// The first `BATCHED_ERROR_CAP` per-element failures.
117        listed: Vec<StorageCoerceError>,
118        /// Total number of failing elements (`>= listed.len()`).
119        total: usize,
120    },
121}
122
123/// The per-element variants carry a 0-based `index` into the Rust vector;
124/// the message numbers the element as R counts the resulting vector:
125/// `value out of range for i64 → i32 (element 2)`.
126impl fmt::Display for StorageCoerceError {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        let at = |index: &Option<usize>| {
129            index
130                .map(crate::from_r::element_position)
131                .unwrap_or_default()
132        };
133        match self {
134            StorageCoerceError::Unsupported { from, to } => {
135                f.write_fmt(format_args!("cannot convert {0} to {1}", from, to))write!(f, "cannot convert {} to {}", from, to)
136            }
137            StorageCoerceError::OutOfRange { from, to, index } => {
138                f.write_fmt(format_args!("value out of range for {1} → {2}{0}", at(index),
        from, to))write!(f, "value out of range for {from} → {to}{}", at(index))
139            }
140            StorageCoerceError::NonFinite { to, index } => {
141                f.write_fmt(format_args!("non-finite value cannot convert to {1}{0}",
        at(index), to))write!(f, "non-finite value cannot convert to {to}{}", at(index))
142            }
143            StorageCoerceError::PrecisionLoss { to, index } => {
144                f.write_fmt(format_args!("value would lose precision converting to {1}{0}",
        at(index), to))write!(
145                    f,
146                    "value would lose precision converting to {to}{}",
147                    at(index)
148                )
149            }
150            StorageCoerceError::NotIntegral { to, index } => {
151                f.write_fmt(format_args!("non-integral value cannot convert to {1}{0}",
        at(index), to))write!(f, "non-integral value cannot convert to {to}{}", at(index))
152            }
153            StorageCoerceError::MissingValue { to, index } => {
154                f.write_fmt(format_args!("missing value cannot convert to {1}{0}", at(index),
        to))write!(f, "missing value cannot convert to {to}{}", at(index))
155            }
156            StorageCoerceError::InvalidUtf8 { index } => {
157                f.write_fmt(format_args!("invalid UTF-8{0}", at(index)))write!(f, "invalid UTF-8{}", at(index))
158            }
159            StorageCoerceError::Batched {
160                container,
161                listed,
162                total,
163            } => {
164                f.write_fmt(format_args!("{0} conversion failed: ", container))write!(f, "{} conversion failed: ", container)?;
165                for (i, e) in listed.iter().enumerate() {
166                    if i > 0 {
167                        f.write_fmt(format_args!("; "))write!(f, "; ")?;
168                    }
169                    f.write_fmt(format_args!("{0}", e))write!(f, "{}", e)?;
170                }
171                if *total > listed.len() {
172                    f.write_fmt(format_args!("; and {0} more", total - listed.len()))write!(f, "; and {} more", total - listed.len())?;
173                }
174                Ok(())
175            }
176        }
177    }
178}
179
180impl std::error::Error for StorageCoerceError {}
181
182impl StorageCoerceError {
183    /// Add index information to the error.
184    #[inline]
185    pub fn at_index(self, idx: usize) -> Self {
186        match self {
187            StorageCoerceError::OutOfRange { from, to, .. } => StorageCoerceError::OutOfRange {
188                from,
189                to,
190                index: Some(idx),
191            },
192            StorageCoerceError::NonFinite { to, .. } => StorageCoerceError::NonFinite {
193                to,
194                index: Some(idx),
195            },
196            StorageCoerceError::PrecisionLoss { to, .. } => StorageCoerceError::PrecisionLoss {
197                to,
198                index: Some(idx),
199            },
200            StorageCoerceError::NotIntegral { to, .. } => StorageCoerceError::NotIntegral {
201                to,
202                index: Some(idx),
203            },
204            StorageCoerceError::MissingValue { to, .. } => StorageCoerceError::MissingValue {
205                to,
206                index: Some(idx),
207            },
208            StorageCoerceError::InvalidUtf8 { .. } => {
209                StorageCoerceError::InvalidUtf8 { index: Some(idx) }
210            }
211            // Batched errors already carry per-element indices on their
212            // `listed` entries — indexing the aggregate itself is a no-op.
213            batched @ StorageCoerceError::Batched { .. } => batched,
214            other => other,
215        }
216    }
217}
218// endregion
219
220// region: Trait definition
221
222/// Storage-directed conversion to R SEXP.
223///
224/// This trait allows converting Rust values to R with an explicit target storage
225/// type. The conversion is value-based: it succeeds if all values fit the target
226/// type, and fails otherwise.
227///
228/// # Target Types
229///
230/// - `i32` → R integer (INTSXP)
231/// - `f64` → R numeric (REALSXP)
232/// - `RLogical` → R logical (LGLSXP)
233/// - `u8` → R raw (RAWSXP)
234/// - `String` → R character (STRSXP)
235///
236/// # Example
237///
238/// ```ignore
239/// use miniextendr_api::IntoRAs;
240///
241/// // Convert i64 to R integer (if values fit)
242/// let x: Vec<i64> = vec![1, 2, 3];
243/// let sexp = x.into_r_as::<i32>()?;
244///
245/// // Convert f64 to R integer (if values are integral)
246/// let y: Vec<f64> = vec![1.0, 2.0, 3.0];
247/// let sexp = y.into_r_as::<i32>()?;
248/// ```
249pub trait IntoRAs<Target> {
250    /// Convert to R SEXP with the specified target storage type.
251    fn into_r_as(self) -> Result<SEXP, StorageCoerceError>;
252}
253// endregion
254
255// region: Helper: try_coerce_scalar with error mapping
256
257/// Try to coerce a scalar value, mapping CoerceError to StorageCoerceError.
258#[inline]
259fn try_coerce_scalar<T, R>(
260    value: T,
261    from: &'static str,
262    to: &'static str,
263) -> Result<R, StorageCoerceError>
264where
265    T: TryCoerce<R>,
266    T::Error: Into<CoerceErrorKind>,
267{
268    value
269        .try_coerce()
270        .map_err(|e| map_coerce_error(e.into(), from, to))
271}
272
273/// Internal enum to unify different coerce error types.
274#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CoerceErrorKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                CoerceErrorKind::Overflow => "Overflow",
                CoerceErrorKind::PrecisionLoss => "PrecisionLoss",
                CoerceErrorKind::NaN => "NaN",
                CoerceErrorKind::Infallible => "Infallible",
            })
    }
}Debug)]
275enum CoerceErrorKind {
276    Overflow,
277    PrecisionLoss,
278    NaN,
279    Infallible,
280}
281
282impl From<CoerceError> for CoerceErrorKind {
283    fn from(e: CoerceError) -> Self {
284        match e {
285            CoerceError::Overflow => CoerceErrorKind::Overflow,
286            CoerceError::PrecisionLoss => CoerceErrorKind::PrecisionLoss,
287            CoerceError::NaN => CoerceErrorKind::NaN,
288            CoerceError::Zero => CoerceErrorKind::Overflow, // Treat zero error as overflow
289        }
290    }
291}
292
293impl From<std::convert::Infallible> for CoerceErrorKind {
294    fn from(_: std::convert::Infallible) -> Self {
295        CoerceErrorKind::Infallible
296    }
297}
298
299fn map_coerce_error(
300    kind: CoerceErrorKind,
301    from: &'static str,
302    to: &'static str,
303) -> StorageCoerceError {
304    match kind {
305        CoerceErrorKind::Overflow => StorageCoerceError::OutOfRange {
306            from,
307            to,
308            index: None,
309        },
310        CoerceErrorKind::PrecisionLoss => StorageCoerceError::PrecisionLoss { to, index: None },
311        CoerceErrorKind::NaN => StorageCoerceError::NonFinite { to, index: None },
312        CoerceErrorKind::Infallible => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
313    }
314}
315// endregion
316
317// region: Scalar implementations: -> i32 (R integer)
318
319macro_rules! impl_into_r_as_scalar {
320    ($target:ty, $target_name:literal; $from:ty, $from_name:literal) => {
321        impl IntoRAs<$target> for $from {
322            #[inline]
323            fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
324                let v: $target = try_coerce_scalar(self, $from_name, $target_name)?;
325                Ok(v.into_sexp())
326            }
327        }
328    };
329}
330
331// Identity
332impl IntoRAs<i32> for i32 {
333    #[inline]
334    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
335        Ok(self.into_sexp())
336    }
337}
338
339// Widening (infallible)
340impl IntoRAs<i32> for i8 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "i8", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; i8, "i8");
341impl IntoRAs<i32> for i16 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "i16", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; i16, "i16");
342impl IntoRAs<i32> for u8 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "u8", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; u8, "u8");
343impl IntoRAs<i32> for u16 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "u16", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; u16, "u16");
344
345// Narrowing (fallible)
346impl IntoRAs<i32> for i64 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "i64", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; i64, "i64");
347impl IntoRAs<i32> for isize {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "isize", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; isize, "isize");
348impl IntoRAs<i32> for u32 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "u32", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; u32, "u32");
349impl IntoRAs<i32> for u64 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "u64", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; u64, "u64");
350impl IntoRAs<i32> for usize {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "usize", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; usize, "usize");
351
352// Float to int (fallible - must be integral and in range)
353impl IntoRAs<i32> for f32 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "f32", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; f32, "f32");
354impl IntoRAs<i32> for f64 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: i32 = try_coerce_scalar(self, "f64", "i32")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(i32, "i32"; f64, "f64");
355
356// Bool to int
357impl IntoRAs<i32> for bool {
358    #[inline]
359    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
360        Ok((self as i32).into_sexp())
361    }
362}
363// endregion
364
365// region: Scalar implementations: -> f64 (R numeric)
366
367// Identity
368impl IntoRAs<f64> for f64 {
369    #[inline]
370    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
371        if !self.is_finite() {
372            return Err(StorageCoerceError::NonFinite {
373                to: "f64",
374                index: None,
375            });
376        }
377        Ok(self.into_sexp())
378    }
379}
380
381// Widening from f32 (check finite)
382impl IntoRAs<f64> for f32 {
383    #[inline]
384    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
385        if !self.is_finite() {
386            return Err(StorageCoerceError::NonFinite {
387                to: "f64",
388                index: None,
389            });
390        }
391        Ok((self as f64).into_sexp())
392    }
393}
394
395// Widening from integers (infallible for small types)
396impl IntoRAs<f64> for i8 {
397    #[inline]
398    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
399        Ok((self as f64).into_sexp())
400    }
401}
402
403impl IntoRAs<f64> for i16 {
404    #[inline]
405    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
406        Ok((self as f64).into_sexp())
407    }
408}
409
410impl IntoRAs<f64> for i32 {
411    #[inline]
412    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
413        // i32::MIN is R's NA_integer_ sentinel. A blind `self as f64` would
414        // turn it into the finite value -2147483648.0, silently destroying the
415        // missing-value semantics. Detect it and surface MissingValue instead.
416        if self.is_na() {
417            return Err(StorageCoerceError::MissingValue {
418                to: "f64",
419                index: None,
420            });
421        }
422        Ok((self as f64).into_sexp())
423    }
424}
425
426impl IntoRAs<f64> for u8 {
427    #[inline]
428    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
429        Ok((self as f64).into_sexp())
430    }
431}
432
433impl IntoRAs<f64> for u16 {
434    #[inline]
435    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
436        Ok((self as f64).into_sexp())
437    }
438}
439
440impl IntoRAs<f64> for u32 {
441    #[inline]
442    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
443        Ok((self as f64).into_sexp())
444    }
445}
446
447// Large integers: check precision (> 2^53 loses precision)
448impl IntoRAs<f64> for i64 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: f64 = try_coerce_scalar(self, "i64", "f64")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(f64, "f64"; i64, "i64");
449impl IntoRAs<f64> for u64 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: f64 = try_coerce_scalar(self, "u64", "f64")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(f64, "f64"; u64, "u64");
450impl IntoRAs<f64> for isize {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: f64 = try_coerce_scalar(self, "isize", "f64")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(f64, "f64"; isize, "isize");
451impl IntoRAs<f64> for usize {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: f64 = try_coerce_scalar(self, "usize", "f64")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(f64, "f64"; usize, "usize");
452
453// Bool to f64
454impl IntoRAs<f64> for bool {
455    #[inline]
456    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
457        Ok((if self { 1.0 } else { 0.0 }).into_sexp())
458    }
459}
460// endregion
461
462// region: Scalar implementations: -> u8 (R raw)
463
464// Identity
465impl IntoRAs<u8> for u8 {
466    #[inline]
467    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
468        Ok(self.into_sexp())
469    }
470}
471
472// Narrowing (fallible)
473impl IntoRAs<u8> for i8 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "i8", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; i8, "i8");
474impl IntoRAs<u8> for i16 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "i16", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; i16, "i16");
475impl IntoRAs<u8> for i32 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "i32", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; i32, "i32");
476impl IntoRAs<u8> for i64 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "i64", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; i64, "i64");
477impl IntoRAs<u8> for isize {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "isize", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; isize, "isize");
478impl IntoRAs<u8> for u16 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "u16", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; u16, "u16");
479impl IntoRAs<u8> for u32 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "u32", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; u32, "u32");
480impl IntoRAs<u8> for u64 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "u64", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; u64, "u64");
481impl IntoRAs<u8> for usize {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "usize", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; usize, "usize");
482impl IntoRAs<u8> for f32 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "f32", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; f32, "f32");
483impl IntoRAs<u8> for f64 {
    #[inline]
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let v: u8 = try_coerce_scalar(self, "f64", "u8")?;
        Ok(v.into_sexp())
    }
}impl_into_r_as_scalar!(u8, "u8"; f64, "f64");
484// endregion
485
486// region: Scalar implementations: -> RLogical (R logical)
487
488impl IntoRAs<RLogical> for bool {
489    #[inline]
490    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
491        Ok(self.into_sexp())
492    }
493}
494
495impl IntoRAs<RLogical> for RLogical {
496    #[inline]
497    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
498        Ok(self.into_sexp())
499    }
500}
501
502// Integer to logical: only 0, 1, NA_INTEGER allowed
503impl IntoRAs<RLogical> for i32 {
504    #[inline]
505    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
506        match self {
507            0 => Ok(false.into_sexp()),
508            1 => Ok(true.into_sexp()),
509            v if v.is_na() => Ok(RLogical::NA.into_sexp()),
510            _ => Err(StorageCoerceError::OutOfRange {
511                from: "i32",
512                to: "RLogical",
513                index: None,
514            }),
515        }
516    }
517}
518// endregion
519
520// region: Scalar implementations: -> String (R character)
521
522impl IntoRAs<String> for String {
523    #[inline]
524    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
525        Ok(self.into_sexp())
526    }
527}
528
529impl IntoRAs<String> for &str {
530    #[inline]
531    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
532        Ok(self.into_sexp())
533    }
534}
535
536// Numeric to String: stringify (including NaN/Inf)
537impl IntoRAs<String> for f64 {
538    #[inline]
539    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
540        let s = if self.is_nan() {
541            "NaN".to_string()
542        } else if self.is_infinite() {
543            if self.is_sign_positive() {
544                "Inf".to_string()
545            } else {
546                "-Inf".to_string()
547            }
548        } else {
549            self.to_string()
550        };
551        Ok(s.into_sexp())
552    }
553}
554
555impl IntoRAs<String> for f32 {
556    #[inline]
557    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
558        <f64 as IntoRAs<String>>::into_r_as(self as f64)
559    }
560}
561
562impl IntoRAs<String> for i32 {
563    #[inline]
564    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
565        Ok(self.to_string().into_sexp())
566    }
567}
568
569impl IntoRAs<String> for i64 {
570    #[inline]
571    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
572        Ok(self.to_string().into_sexp())
573    }
574}
575
576impl IntoRAs<String> for bool {
577    #[inline]
578    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
579        Ok((if self { "TRUE" } else { "FALSE" }).into_sexp())
580    }
581}
582
583impl IntoRAs<String> for RLogical {
584    #[inline]
585    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
586        let s = match self.to_option_bool() {
587            None => "NA",
588            Some(true) => "TRUE",
589            Some(false) => "FALSE",
590        };
591        Ok(s.into_sexp())
592    }
593}
594// endregion
595
596// region: Vec implementations: -> i32 (R integer vector)
597
598macro_rules! impl_vec_into_r_as {
599    ($target:ty, $target_name:literal; $from:ty, $from_name:literal) => {
600        impl IntoRAs<$target> for Vec<$from> {
601            fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
602                let mut result = Vec::with_capacity(self.len());
603                let mut listed: Vec<StorageCoerceError> = Vec::new();
604                let mut total = 0usize;
605                for (i, val) in self.into_iter().enumerate() {
606                    match try_coerce_scalar::<$from, $target>(val, $from_name, $target_name) {
607                        Ok(v) => result.push(v),
608                        Err(e) => {
609                            if listed.len() < BATCHED_ERROR_CAP {
610                                listed.push(e.at_index(i));
611                            }
612                            total += 1;
613                        }
614                    }
615                }
616                if total > 0 {
617                    return Err(StorageCoerceError::Batched {
618                        container: concat!("Vec<", $from_name, ">"),
619                        listed,
620                        total,
621                    });
622                }
623                Ok(result.into_sexp())
624            }
625        }
626
627        impl IntoRAs<$target> for &[$from] {
628            fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
629                let mut result = Vec::with_capacity(self.len());
630                let mut listed: Vec<StorageCoerceError> = Vec::new();
631                let mut total = 0usize;
632                for (i, &val) in self.iter().enumerate() {
633                    match try_coerce_scalar::<$from, $target>(val, $from_name, $target_name) {
634                        Ok(v) => result.push(v),
635                        Err(e) => {
636                            if listed.len() < BATCHED_ERROR_CAP {
637                                listed.push(e.at_index(i));
638                            }
639                            total += 1;
640                        }
641                    }
642                }
643                if total > 0 {
644                    return Err(StorageCoerceError::Batched {
645                        container: concat!("&[", $from_name, "]"),
646                        listed,
647                        total,
648                    });
649                }
650                Ok(result.into_sexp())
651            }
652        }
653    };
654}
655
656// Identity (direct copy)
657impl IntoRAs<i32> for Vec<i32> {
658    #[inline]
659    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
660        Ok(self.into_sexp())
661    }
662}
663
664impl IntoRAs<i32> for &[i32] {
665    #[inline]
666    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
667        Ok(self.into_sexp())
668    }
669}
670
671impl IntoRAs<i32> for Vec<i8> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<i8, i32>(val, "i8", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<i8>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[i8] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<i8, i32>(val, "i8", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[i8]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; i8, "i8");
672impl IntoRAs<i32> for Vec<i16> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<i16, i32>(val, "i16", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<i16>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[i16] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<i16, i32>(val, "i16", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[i16]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; i16, "i16");
673impl IntoRAs<i32> for Vec<u8> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<u8, i32>(val, "u8", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<u8>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[u8] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<u8, i32>(val, "u8", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[u8]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; u8, "u8");
674impl IntoRAs<i32> for Vec<u16> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<u16, i32>(val, "u16", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<u16>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[u16] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<u16, i32>(val, "u16", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[u16]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; u16, "u16");
675impl IntoRAs<i32> for Vec<i64> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<i64, i32>(val, "i64", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<i64>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[i64] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<i64, i32>(val, "i64", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[i64]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; i64, "i64");
676impl IntoRAs<i32> for Vec<isize> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<isize, i32>(val, "isize", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<isize>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[isize] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<isize, i32>(val, "isize", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[isize]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; isize, "isize");
677impl IntoRAs<i32> for Vec<u32> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<u32, i32>(val, "u32", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<u32>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[u32] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<u32, i32>(val, "u32", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[u32]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; u32, "u32");
678impl IntoRAs<i32> for Vec<u64> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<u64, i32>(val, "u64", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<u64>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[u64] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<u64, i32>(val, "u64", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[u64]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; u64, "u64");
679impl IntoRAs<i32> for Vec<usize> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<usize, i32>(val, "usize", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<usize>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[usize] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<usize, i32>(val, "usize", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[usize]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; usize, "usize");
680impl IntoRAs<i32> for Vec<f32> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<f32, i32>(val, "f32", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<f32>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[f32] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<f32, i32>(val, "f32", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[f32]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; f32, "f32");
681impl IntoRAs<i32> for Vec<f64> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<f64, i32>(val, "f64", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<f64>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<i32> for &[f64] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<f64, i32>(val, "f64", "i32") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[f64]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(i32, "i32"; f64, "f64");
682
683// Vec<bool> -> i32
684impl IntoRAs<i32> for Vec<bool> {
685    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
686        let result: Vec<i32> = self.into_iter().map(|b| b as i32).collect();
687        Ok(result.into_sexp())
688    }
689}
690
691impl IntoRAs<i32> for &[bool] {
692    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
693        let result: Vec<i32> = self.iter().map(|&b| b as i32).collect();
694        Ok(result.into_sexp())
695    }
696}
697// endregion
698
699// region: Vec implementations: -> f64 (R numeric vector)
700
701// Identity - but check for finite values
702impl IntoRAs<f64> for Vec<f64> {
703    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
704        let mut listed: Vec<StorageCoerceError> = Vec::new();
705        let mut total = 0usize;
706        for (i, &val) in self.iter().enumerate() {
707            if !val.is_finite() {
708                if listed.len() < BATCHED_ERROR_CAP {
709                    listed.push(StorageCoerceError::NonFinite {
710                        to: "f64",
711                        index: Some(i),
712                    });
713                }
714                total += 1;
715            }
716        }
717        if total > 0 {
718            return Err(StorageCoerceError::Batched {
719                container: "Vec<f64>",
720                listed,
721                total,
722            });
723        }
724        Ok(self.into_sexp())
725    }
726}
727
728impl IntoRAs<f64> for &[f64] {
729    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
730        let mut listed: Vec<StorageCoerceError> = Vec::new();
731        let mut total = 0usize;
732        for (i, &val) in self.iter().enumerate() {
733            if !val.is_finite() {
734                if listed.len() < BATCHED_ERROR_CAP {
735                    listed.push(StorageCoerceError::NonFinite {
736                        to: "f64",
737                        index: Some(i),
738                    });
739                }
740                total += 1;
741            }
742        }
743        if total > 0 {
744            return Err(StorageCoerceError::Batched {
745                container: "&[f64]",
746                listed,
747                total,
748            });
749        }
750        Ok(self.into_sexp())
751    }
752}
753
754// f32 - check finite then widen
755impl IntoRAs<f64> for Vec<f32> {
756    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
757        let mut result = Vec::with_capacity(self.len());
758        let mut listed: Vec<StorageCoerceError> = Vec::new();
759        let mut total = 0usize;
760        for (i, val) in self.into_iter().enumerate() {
761            if !val.is_finite() {
762                if listed.len() < BATCHED_ERROR_CAP {
763                    listed.push(StorageCoerceError::NonFinite {
764                        to: "f64",
765                        index: Some(i),
766                    });
767                }
768                total += 1;
769            } else {
770                result.push(val as f64);
771            }
772        }
773        if total > 0 {
774            return Err(StorageCoerceError::Batched {
775                container: "Vec<f32>",
776                listed,
777                total,
778            });
779        }
780        Ok(result.into_sexp())
781    }
782}
783
784impl IntoRAs<f64> for &[f32] {
785    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
786        let mut result = Vec::with_capacity(self.len());
787        let mut listed: Vec<StorageCoerceError> = Vec::new();
788        let mut total = 0usize;
789        for (i, &val) in self.iter().enumerate() {
790            if !val.is_finite() {
791                if listed.len() < BATCHED_ERROR_CAP {
792                    listed.push(StorageCoerceError::NonFinite {
793                        to: "f64",
794                        index: Some(i),
795                    });
796                }
797                total += 1;
798            } else {
799                result.push(val as f64);
800            }
801        }
802        if total > 0 {
803            return Err(StorageCoerceError::Batched {
804                container: "&[f32]",
805                listed,
806                total,
807            });
808        }
809        Ok(result.into_sexp())
810    }
811}
812
813// Small integers - infallible widening
814macro_rules! impl_vec_into_r_as_f64_infallible {
815    ($from:ty) => {
816        impl IntoRAs<f64> for Vec<$from> {
817            fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
818                let result: Vec<f64> = self.into_iter().map(|v| v as f64).collect();
819                Ok(result.into_sexp())
820            }
821        }
822
823        impl IntoRAs<f64> for &[$from] {
824            fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
825                let result: Vec<f64> = self.iter().map(|&v| v as f64).collect();
826                Ok(result.into_sexp())
827            }
828        }
829    };
830}
831
832impl IntoRAs<f64> for Vec<i8> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.into_iter().map(|v| v as f64).collect();
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[i8] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.iter().map(|&v| v as f64).collect();
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as_f64_infallible!(i8);
833impl IntoRAs<f64> for Vec<i16> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.into_iter().map(|v| v as f64).collect();
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[i16] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.iter().map(|&v| v as f64).collect();
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as_f64_infallible!(i16);
834impl IntoRAs<f64> for Vec<u8> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.into_iter().map(|v| v as f64).collect();
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[u8] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.iter().map(|&v| v as f64).collect();
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as_f64_infallible!(u8);
835impl IntoRAs<f64> for Vec<u16> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.into_iter().map(|v| v as f64).collect();
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[u16] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.iter().map(|&v| v as f64).collect();
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as_f64_infallible!(u16);
836impl IntoRAs<f64> for Vec<u32> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.into_iter().map(|v| v as f64).collect();
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[u32] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let result: Vec<f64> = self.iter().map(|&v| v as f64).collect();
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as_f64_infallible!(u32);
837
838// i32 -> f64: widening is value-preserving EXCEPT for i32::MIN, which is R's
839// NA_integer_ sentinel. Casting it would yield the finite -2147483648.0 and
840// silently drop the missing-value semantics, so detect it and error instead.
841impl IntoRAs<f64> for Vec<i32> {
842    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
843        let mut result = Vec::with_capacity(self.len());
844        let mut listed: Vec<StorageCoerceError> = Vec::new();
845        let mut total = 0usize;
846        for (i, val) in self.into_iter().enumerate() {
847            if val.is_na() {
848                if listed.len() < BATCHED_ERROR_CAP {
849                    listed.push(StorageCoerceError::MissingValue {
850                        to: "f64",
851                        index: Some(i),
852                    });
853                }
854                total += 1;
855            } else {
856                result.push(val as f64);
857            }
858        }
859        if total > 0 {
860            return Err(StorageCoerceError::Batched {
861                container: "Vec<i32>",
862                listed,
863                total,
864            });
865        }
866        Ok(result.into_sexp())
867    }
868}
869
870impl IntoRAs<f64> for &[i32] {
871    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
872        let mut result = Vec::with_capacity(self.len());
873        let mut listed: Vec<StorageCoerceError> = Vec::new();
874        let mut total = 0usize;
875        for (i, &val) in self.iter().enumerate() {
876            if val.is_na() {
877                if listed.len() < BATCHED_ERROR_CAP {
878                    listed.push(StorageCoerceError::MissingValue {
879                        to: "f64",
880                        index: Some(i),
881                    });
882                }
883                total += 1;
884            } else {
885                result.push(val as f64);
886            }
887        }
888        if total > 0 {
889            return Err(StorageCoerceError::Batched {
890                container: "&[i32]",
891                listed,
892                total,
893            });
894        }
895        Ok(result.into_sexp())
896    }
897}
898
899// Large integers - check precision
900impl IntoRAs<f64> for Vec<i64> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<i64, f64>(val, "i64", "f64") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<i64>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[i64] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<i64, f64>(val, "i64", "f64") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[i64]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(f64, "f64"; i64, "i64");
901impl IntoRAs<f64> for Vec<u64> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<u64, f64>(val, "u64", "f64") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<u64>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[u64] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<u64, f64>(val, "u64", "f64") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[u64]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(f64, "f64"; u64, "u64");
902impl IntoRAs<f64> for Vec<isize> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<isize, f64>(val, "isize", "f64") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<isize>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[isize] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<isize, f64>(val, "isize", "f64") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[isize]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(f64, "f64"; isize, "isize");
903impl IntoRAs<f64> for Vec<usize> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<usize, f64>(val, "usize", "f64") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<usize>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<f64> for &[usize] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<usize, f64>(val, "usize", "f64") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[usize]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(f64, "f64"; usize, "usize");
904
905// Vec<bool> -> f64
906impl IntoRAs<f64> for Vec<bool> {
907    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
908        let result: Vec<f64> = self
909            .into_iter()
910            .map(|b| if b { 1.0 } else { 0.0 })
911            .collect();
912        Ok(result.into_sexp())
913    }
914}
915
916impl IntoRAs<f64> for &[bool] {
917    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
918        let result: Vec<f64> = self.iter().map(|&b| if b { 1.0 } else { 0.0 }).collect();
919        Ok(result.into_sexp())
920    }
921}
922// endregion
923
924// region: Vec implementations: -> u8 (R raw vector)
925
926// Identity
927impl IntoRAs<u8> for Vec<u8> {
928    #[inline]
929    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
930        Ok(self.into_sexp())
931    }
932}
933
934impl IntoRAs<u8> for &[u8] {
935    #[inline]
936    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
937        Ok(self.into_sexp())
938    }
939}
940
941impl IntoRAs<u8> for Vec<i8> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<i8, u8>(val, "i8", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<i8>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[i8] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<i8, u8>(val, "i8", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[i8]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; i8, "i8");
942impl IntoRAs<u8> for Vec<i16> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<i16, u8>(val, "i16", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<i16>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[i16] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<i16, u8>(val, "i16", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[i16]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; i16, "i16");
943impl IntoRAs<u8> for Vec<i32> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<i32, u8>(val, "i32", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<i32>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[i32] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<i32, u8>(val, "i32", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[i32]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; i32, "i32");
944impl IntoRAs<u8> for Vec<i64> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<i64, u8>(val, "i64", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<i64>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[i64] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<i64, u8>(val, "i64", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[i64]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; i64, "i64");
945impl IntoRAs<u8> for Vec<isize> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<isize, u8>(val, "isize", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<isize>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[isize] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<isize, u8>(val, "isize", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[isize]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; isize, "isize");
946impl IntoRAs<u8> for Vec<u16> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<u16, u8>(val, "u16", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<u16>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[u16] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<u16, u8>(val, "u16", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[u16]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; u16, "u16");
947impl IntoRAs<u8> for Vec<u32> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<u32, u8>(val, "u32", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<u32>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[u32] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<u32, u8>(val, "u32", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[u32]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; u32, "u32");
948impl IntoRAs<u8> for Vec<u64> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<u64, u8>(val, "u64", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<u64>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[u64] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<u64, u8>(val, "u64", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[u64]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; u64, "u64");
949impl IntoRAs<u8> for Vec<usize> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<usize, u8>(val, "usize", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<usize>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[usize] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<usize, u8>(val, "usize", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[usize]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; usize, "usize");
950impl IntoRAs<u8> for Vec<f32> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<f32, u8>(val, "f32", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<f32>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[f32] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<f32, u8>(val, "f32", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[f32]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; f32, "f32");
951impl IntoRAs<u8> for Vec<f64> {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, val) in self.into_iter().enumerate() {
            match try_coerce_scalar::<f64, u8>(val, "f64", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "Vec<f64>",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}
impl IntoRAs<u8> for &[f64] {
    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
        let mut result = Vec::with_capacity(self.len());
        let mut listed: Vec<StorageCoerceError> = Vec::new();
        let mut total = 0usize;
        for (i, &val) in self.iter().enumerate() {
            match try_coerce_scalar::<f64, u8>(val, "f64", "u8") {
                Ok(v) => result.push(v),
                Err(e) => {
                    if listed.len() < BATCHED_ERROR_CAP {
                        listed.push(e.at_index(i));
                    }
                    total += 1;
                }
            }
        }
        if total > 0 {
            return Err(StorageCoerceError::Batched {
                        container: "&[f64]",
                        listed,
                        total,
                    });
        }
        Ok(result.into_sexp())
    }
}impl_vec_into_r_as!(u8, "u8"; f64, "f64");
952// endregion
953
954// region: Vec implementations: -> RLogical (R logical vector)
955
956impl IntoRAs<RLogical> for Vec<bool> {
957    #[inline]
958    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
959        Ok(self.into_sexp())
960    }
961}
962
963impl IntoRAs<RLogical> for &[bool] {
964    #[inline]
965    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
966        Ok(self.into_sexp())
967    }
968}
969// endregion
970
971// region: Vec implementations: -> String (R character vector)
972
973impl IntoRAs<String> for Vec<String> {
974    #[inline]
975    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
976        Ok(self.into_sexp())
977    }
978}
979
980impl IntoRAs<String> for &[String] {
981    #[inline]
982    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
983        Ok(self.into_sexp())
984    }
985}
986
987impl IntoRAs<String> for Vec<&str> {
988    #[inline]
989    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
990        Ok(self.into_sexp())
991    }
992}
993
994impl IntoRAs<String> for &[&str] {
995    #[inline]
996    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
997        Ok(self.into_sexp())
998    }
999}
1000
1001// Numeric vectors to String (stringify)
1002impl IntoRAs<String> for Vec<f64> {
1003    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
1004        let strings: Vec<String> = self
1005            .into_iter()
1006            .map(|v| {
1007                if v.is_nan() {
1008                    "NaN".to_string()
1009                } else if v.is_infinite() {
1010                    if v.is_sign_positive() {
1011                        "Inf".to_string()
1012                    } else {
1013                        "-Inf".to_string()
1014                    }
1015                } else {
1016                    v.to_string()
1017                }
1018            })
1019            .collect();
1020        Ok(strings.into_sexp())
1021    }
1022}
1023
1024impl IntoRAs<String> for Vec<i32> {
1025    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
1026        let strings: Vec<String> = self.into_iter().map(|v| v.to_string()).collect();
1027        Ok(strings.into_sexp())
1028    }
1029}
1030
1031impl IntoRAs<String> for Vec<i64> {
1032    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
1033        let strings: Vec<String> = self.into_iter().map(|v| v.to_string()).collect();
1034        Ok(strings.into_sexp())
1035    }
1036}
1037
1038impl IntoRAs<String> for Vec<bool> {
1039    fn into_r_as(self) -> Result<SEXP, StorageCoerceError> {
1040        let strings: Vec<String> = self
1041            .into_iter()
1042            .map(|b| if b { "TRUE" } else { "FALSE" }.to_string())
1043            .collect();
1044        Ok(strings.into_sexp())
1045    }
1046}
1047// endregion
1048
1049// region: Tests
1050
1051#[cfg(test)]
1052mod tests {
1053    use super::*;
1054
1055    // Note: These are compile-time tests to ensure the trait is implemented correctly.
1056    // Runtime tests require R to be initialized and should go in the integration tests.
1057
1058    fn _assert_into_r_as<T, Target>()
1059    where
1060        T: IntoRAs<Target>,
1061    {
1062    }
1063
1064    #[test]
1065    fn test_trait_bounds() {
1066        // Scalars -> i32
1067        _assert_into_r_as::<i32, i32>();
1068        _assert_into_r_as::<i64, i32>();
1069        _assert_into_r_as::<f64, i32>();
1070        _assert_into_r_as::<bool, i32>();
1071
1072        // Scalars -> f64
1073        _assert_into_r_as::<f64, f64>();
1074        _assert_into_r_as::<i32, f64>();
1075        _assert_into_r_as::<i64, f64>();
1076        _assert_into_r_as::<bool, f64>();
1077
1078        // Scalars -> u8
1079        _assert_into_r_as::<u8, u8>();
1080        _assert_into_r_as::<i32, u8>();
1081
1082        // Scalars -> RLogical
1083        _assert_into_r_as::<bool, RLogical>();
1084        _assert_into_r_as::<i32, RLogical>();
1085
1086        // Scalars -> String
1087        _assert_into_r_as::<String, String>();
1088        _assert_into_r_as::<&str, String>();
1089        _assert_into_r_as::<f64, String>();
1090        _assert_into_r_as::<i32, String>();
1091        _assert_into_r_as::<bool, String>();
1092
1093        // Vecs -> i32
1094        _assert_into_r_as::<Vec<i32>, i32>();
1095        _assert_into_r_as::<Vec<i64>, i32>();
1096        _assert_into_r_as::<Vec<f64>, i32>();
1097
1098        // Vecs -> f64
1099        _assert_into_r_as::<Vec<f64>, f64>();
1100        _assert_into_r_as::<Vec<i32>, f64>();
1101        _assert_into_r_as::<Vec<i64>, f64>();
1102
1103        // Vecs -> u8
1104        _assert_into_r_as::<Vec<u8>, u8>();
1105        _assert_into_r_as::<Vec<i32>, u8>();
1106
1107        // Vecs -> String
1108        _assert_into_r_as::<Vec<String>, String>();
1109        _assert_into_r_as::<Vec<f64>, String>();
1110    }
1111
1112    // Batched-error tests (#1097). These only exercise the failure path
1113    // (`into_sexp()` is never reached), so they don't need `with_r_thread` —
1114    // unlike the success-path integration tests in `conversion_coverage.rs`.
1115
1116    /// Failures at indices 3, 17, 42 must all be listed in one `Batched`
1117    /// error's `Display` string, not just the first one.
1118    #[test]
1119    fn batched_vec_i64_to_i32_lists_all_failing_indices() {
1120        let mut v = vec![0i64; 43];
1121        for &i in &[3usize, 17, 42] {
1122            v[i] = i64::MAX;
1123        }
1124        let result = IntoRAs::<i32>::into_r_as(v);
1125        match result {
1126            Err(StorageCoerceError::Batched {
1127                container,
1128                listed,
1129                total,
1130            }) => {
1131                assert_eq!(container, "Vec<i64>");
1132                assert_eq!(total, 3);
1133                assert_eq!(listed.len(), 3);
1134                let display = StorageCoerceError::Batched {
1135                    container,
1136                    listed,
1137                    total,
1138                }
1139                .to_string();
1140                // The 0-based `index` fields read 1-based, as R numbers the result.
1141                assert!(display.contains("(element 4)"), "{display}");
1142                assert!(display.contains("(element 18)"), "{display}");
1143                assert!(display.contains("(element 43)"), "{display}");
1144                assert!(!display.contains("index"), "{display}");
1145            }
1146            other => panic!("expected Batched, got {other:?}"),
1147        }
1148    }
1149
1150    /// A 15-failure input lists only the first `BATCHED_ERROR_CAP` (10) and
1151    /// summarizes the rest as `"and 5 more"`.
1152    #[test]
1153    fn batched_vec_i64_to_i32_caps_listed_and_summarizes_rest() {
1154        let v = vec![i64::MAX; 15];
1155        let result = IntoRAs::<i32>::into_r_as(v);
1156        match result {
1157            Err(e @ StorageCoerceError::Batched { .. }) => {
1158                if let StorageCoerceError::Batched { listed, total, .. } = &e {
1159                    assert_eq!(*total, 15);
1160                    assert_eq!(listed.len(), BATCHED_ERROR_CAP);
1161                }
1162                let display = e.to_string();
1163                assert!(display.contains("and 5 more"), "{display}");
1164            }
1165            other => panic!("expected Batched, got {other:?}"),
1166        }
1167    }
1168
1169    /// The `&[T]` slice arms carry their own container label (`"&[i64]"`,
1170    /// not `"Vec<i64>"`) and batch failures the same way as the `Vec` arms.
1171    #[test]
1172    fn batched_slice_i64_to_i32_lists_all_failing_indices() {
1173        let mut v = vec![0i64; 6];
1174        v[1] = i64::MAX;
1175        v[4] = i64::MIN;
1176        let result = IntoRAs::<i32>::into_r_as(v.as_slice());
1177        match result {
1178            Err(e @ StorageCoerceError::Batched { .. }) => {
1179                if let StorageCoerceError::Batched {
1180                    container,
1181                    listed,
1182                    total,
1183                } = &e
1184                {
1185                    assert_eq!(*container, "&[i64]");
1186                    assert_eq!(*total, 2);
1187                    assert_eq!(listed.len(), 2);
1188                }
1189                let display = e.to_string();
1190                assert!(display.contains("&[i64] conversion failed"), "{display}");
1191                assert!(display.contains("(element 2)"), "{display}");
1192                assert!(display.contains("(element 5)"), "{display}");
1193            }
1194            other => panic!("expected Batched, got {other:?}"),
1195        }
1196    }
1197
1198    /// Scalar conversion failures must keep the plain (non-`Batched`) shape
1199    /// and Display grammar — only vector conversions batch.
1200    #[test]
1201    fn scalar_failure_display_unchanged() {
1202        let result = IntoRAs::<i32>::into_r_as(i64::MAX);
1203        match result {
1204            Err(e @ StorageCoerceError::OutOfRange { .. }) => {
1205                assert_eq!(e.to_string(), "value out of range for i64 → i32");
1206            }
1207            other => panic!("expected scalar OutOfRange, got {other:?}"),
1208        }
1209    }
1210}
1211// endregion