Skip to main content

miniextendr_api/
coerce.rs

1//! Type coercion traits for converting Rust types to R native types.
2//!
3//! R has a fixed set of native scalar types:
4//! - `i32` (INTSXP) - 32-bit signed integer
5//! - `f64` (REALSXP) - 64-bit floating point
6//! - `RLogical` (LGLSXP) - logical (TRUE/FALSE/NA)
7//! - `u8` (RAWSXP) - raw bytes
8//! - `Rcomplex` (CPLXSXP) - complex numbers
9//!
10//! # Traits
11//!
12//! - [`Coerce<R>`] - infallible coercion (identity, widening)
13//! - [`TryCoerce<R>`] - fallible coercion (narrowing, overflow-possible)
14//!
15//! # Where this fits
16//!
17//! `Coerce` / `TryCoerce` is the **looser** inbound side — it's the path
18//! [`crate::from_r::TryFromSexp`] impls take for multi-source scalars
19//! (`i8`/`i16`/`u16`/`u32`/`f32`/`i64`/`u64`/`isize`/`usize`). The strict
20//! inbound alternative is the bare `TryFromSexp` impl on the matching R
21//! native type (`i32`, `f64`, …), which rejects mismatched
22//! [`SEXPTYPE`](crate::SEXPTYPE)s outright. Failure mode of relying on
23//! coercion in a function that should be strict: a Rust `i32` argument
24//! silently accepts `1.7` (REALSXP) and truncates.
25//!
26//! The outbound strict-vs-lax pairing lives on [`crate::into_r::IntoR`] (lax,
27//! default) vs [`crate::strict`] (`#[miniextendr(strict)]` opt-in).
28//!
29//! # Examples
30//!
31//! ```ignore
32//! use miniextendr_api::coerce::Coerce;
33//!
34//! // Scalar coercion
35//! let x: i32 = 42i8.coerce();
36//!
37//! // Element-wise slice coercion
38//! let slice: &[i8] = &[1, 2, 3];
39//! let vec: Vec<i32> = slice.coerce();
40//! ```
41
42use crate::altrep_traits::{NA_INTEGER, NA_LOGICAL, NA_REAL};
43use crate::{IsNA, Rboolean, Rcomplex};
44
45/// Infallible coercion from `Self` to type `R`.
46///
47/// Implement this trait for types that can always be converted to `R`.
48/// Identity and widening conversions should use this trait.
49///
50/// Pair: [`TryCoerce`] for fallible (narrowing) coercions. Strict alternative
51/// on the inbound side: [`crate::from_r::TryFromSexp`].
52///
53/// Works for both scalars and element-wise on slices:
54/// - `i8::coerce() -> i32` (scalar widening)
55/// - `&[i8]::coerce() -> Vec<i32>` (element-wise)
56///
57/// # Example
58///
59/// ```ignore
60/// impl Coerce<i32> for MyType {
61///     fn coerce(self) -> i32 { ... }
62/// }
63/// ```
64pub trait Coerce<R> {
65    /// Convert `self` into `R`.
66    ///
67    /// This conversion must not fail.
68    fn coerce(self) -> R;
69}
70
71/// Fallible coercion from `Self` to type `R`.
72///
73/// Implement this trait for narrowing conversions that may overflow or lose precision.
74///
75/// Pair: [`Coerce`] for infallible (widening / identity) coercions. Strict
76/// alternative on the inbound side: [`crate::from_r::TryFromSexp`].
77pub trait TryCoerce<R> {
78    /// Error returned when coercion fails.
79    type Error;
80    /// Attempt to convert `self` into `R`.
81    fn try_coerce(self) -> Result<R, Self::Error>;
82}
83
84/// Error type for coercion failures.
85#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CoerceError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                CoerceError::Overflow => "Overflow",
                CoerceError::PrecisionLoss => "PrecisionLoss",
                CoerceError::NaN => "NaN",
                CoerceError::Zero => "Zero",
            })
    }
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for CoerceError { }
#[automatically_derived]
impl ::core::clone::Clone for CoerceError {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CoerceError { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for CoerceError { }
#[automatically_derived]
impl ::core::cmp::PartialEq for CoerceError {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CoerceError { }Eq)]
86pub enum CoerceError {
87    /// The value cannot fit in the destination range.
88    Overflow,
89    /// The destination type cannot represent this value exactly.
90    PrecisionLoss,
91    /// The input was NaN and destination disallows it.
92    NaN,
93    /// Zero is not allowed by the conversion rule.
94    Zero,
95}
96
97impl std::fmt::Display for CoerceError {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        match self {
100            CoerceError::Overflow => f.write_fmt(format_args!("value out of range"))write!(f, "value out of range"),
101            CoerceError::PrecisionLoss => f.write_fmt(format_args!("precision loss"))write!(f, "precision loss"),
102            CoerceError::NaN => f.write_fmt(format_args!("NaN cannot be converted"))write!(f, "NaN cannot be converted"),
103            CoerceError::Zero => f.write_fmt(format_args!("zero not allowed"))write!(f, "zero not allowed"),
104        }
105    }
106}
107
108impl std::error::Error for CoerceError {}
109
110// region: Blanket: Coerce implies TryCoerce
111
112impl<T, R> TryCoerce<R> for T
113where
114    T: Coerce<R>,
115{
116    type Error = std::convert::Infallible;
117
118    #[inline(always)]
119    fn try_coerce(self) -> Result<R, Self::Error> {
120        Ok(self.coerce())
121    }
122}
123// endregion
124
125// region: Identity coercions
126//
127// Note: Can't use blanket `impl<T> Coerce<T> for T` because it would conflict
128// with container coercion impls like `impl<T: Coerce<R>> Coerce<Vec<R>> for Vec<T>`.
129// Both would apply to `Vec<T>: Coerce<Vec<T>>`, causing overlap.
130
131macro_rules! impl_identity {
132    ($t:ty) => {
133        impl Coerce<$t> for $t {
134            #[inline(always)]
135            fn coerce(self) -> $t {
136                self
137            }
138        }
139    };
140}
141
142impl Coerce<i32> for i32 {
    #[inline(always)]
    fn coerce(self) -> i32 { self }
}impl_identity!(i32);
143impl Coerce<f64> for f64 {
    #[inline(always)]
    fn coerce(self) -> f64 { self }
}impl_identity!(f64);
144impl Coerce<Rboolean> for Rboolean {
    #[inline(always)]
    fn coerce(self) -> Rboolean { self }
}impl_identity!(Rboolean);
145impl Coerce<u8> for u8 {
    #[inline(always)]
    fn coerce(self) -> u8 { self }
}impl_identity!(u8);
146impl Coerce<Rcomplex> for Rcomplex {
    #[inline(always)]
    fn coerce(self) -> Rcomplex { self }
}impl_identity!(Rcomplex);
147// endregion
148
149// region: Scalar coercion macros (shared by the widening / narrowing blocks below)
150
151/// Implement infallible widening `Coerce<$to>` for each `$from => $to` pair via `Into`.
152///
153/// For widenings that have no `From` impl (platform-dependent `isize`/`usize`,
154/// lossy int→float), write the `impl Coerce` by hand with an `as` cast instead.
155macro_rules! impl_widen {
156    ($($from:ty => $to:ty),+ $(,)?) => {
157        $(
158            impl Coerce<$to> for $from {
159                #[inline(always)]
160                fn coerce(self) -> $to {
161                    self.into()
162                }
163            }
164        )+
165    };
166}
167
168/// Implement fallible narrowing `TryCoerce<$target>` for each `$from` via
169/// `try_into` (overflow → `CoerceError::Overflow`).
170macro_rules! impl_try_narrow {
171    ($target:ty; $($from:ty),+ $(,)?) => {
172        $(
173            impl TryCoerce<$target> for $from {
174                type Error = CoerceError;
175                #[inline]
176                fn try_coerce(self) -> Result<$target, CoerceError> {
177                    self.try_into().map_err(|_| CoerceError::Overflow)
178                }
179            }
180        )+
181    };
182}
183// endregion
184
185// region: Widening conversions (blanket impls using marker traits)
186
187/// Blanket impl: Any type that widens to i32 can be coerced to i32.
188///
189/// This replaces individual macro invocations with a single blanket impl.
190/// Covers: i8, i16, u8, u16 (all types where T: `Into<i32>`).
191impl<T: crate::markers::WidensToI32> Coerce<i32> for T {
192    #[inline(always)]
193    fn coerce(self) -> i32 {
194        self.into()
195    }
196}
197
198/// Blanket impl: Any type that widens to f64 can be coerced to f64.
199///
200/// This replaces individual macro invocations with a single blanket impl.
201/// Covers: f32, i8, i16, i32, u8, u16, u32 (all types where T: `Into<f64>`).
202impl<T: crate::markers::WidensToF64> Coerce<f64> for T {
203    #[inline(always)]
204    fn coerce(self) -> f64 {
205        self.into()
206    }
207}
208// endregion
209
210// region: Widening from u8 to larger integer/float types
211
212impl Coerce<i64> for u8 {
    #[inline(always)]
    fn coerce(self) -> i64 { self.into() }
}
impl Coerce<isize> for u8 {
    #[inline(always)]
    fn coerce(self) -> isize { self.into() }
}
impl Coerce<u64> for u8 {
    #[inline(always)]
    fn coerce(self) -> u64 { self.into() }
}
impl Coerce<usize> for u8 {
    #[inline(always)]
    fn coerce(self) -> usize { self.into() }
}
impl Coerce<f32> for u8 {
    #[inline(always)]
    fn coerce(self) -> f32 { self.into() }
}impl_widen!(u8 => i64, u8 => isize, u8 => u64, u8 => usize, u8 => f32);
213
214impl Coerce<f32> for i32 {
215    #[inline(always)]
216    fn coerce(self) -> f32 {
217        self as f32
218    }
219}
220// endregion
221
222// region: bool coercions
223
224impl Coerce<Rboolean> for bool {
225    #[inline(always)]
226    fn coerce(self) -> Rboolean {
227        if self {
228            Rboolean::TRUE
229        } else {
230            Rboolean::FALSE
231        }
232    }
233}
234
235impl Coerce<i32> for bool {
236    #[inline(always)]
237    fn coerce(self) -> i32 {
238        if self { 1 } else { 0 }
239    }
240}
241
242impl Coerce<f64> for bool {
243    #[inline(always)]
244    fn coerce(self) -> f64 {
245        if self { 1.0 } else { 0.0 }
246    }
247}
248
249impl Coerce<i32> for Rboolean {
250    #[inline(always)]
251    fn coerce(self) -> i32 {
252        self as i32
253    }
254}
255// endregion
256
257// region: Option<T> to R-native with None → NA
258
259/// `Option<f64>` → `f64` with `None` → `NA_real_`.
260impl Coerce<f64> for Option<f64> {
261    #[inline(always)]
262    fn coerce(self) -> f64 {
263        self.unwrap_or(NA_REAL)
264    }
265}
266
267/// `Option<i32>` → `i32` with `None` → `NA_integer_`.
268impl Coerce<i32> for Option<i32> {
269    #[inline(always)]
270    fn coerce(self) -> i32 {
271        self.unwrap_or(NA_INTEGER)
272    }
273}
274
275/// `Option<bool>` → `i32` with `None` → `NA_LOGICAL`.
276impl Coerce<i32> for Option<bool> {
277    #[inline(always)]
278    fn coerce(self) -> i32 {
279        match self {
280            Some(true) => 1,
281            Some(false) => 0,
282            None => NA_LOGICAL,
283        }
284    }
285}
286
287/// `Option<Rboolean>` → `i32` with `None` → `NA_LOGICAL`.
288impl Coerce<i32> for Option<Rboolean> {
289    #[inline(always)]
290    fn coerce(self) -> i32 {
291        match self {
292            Some(v) => v as i32,
293            None => NA_LOGICAL,
294        }
295    }
296}
297// endregion
298
299// region: i32 to larger/unsigned types (for argument coercion from R integers)
300
301/// i32 -> i64: widening, always safe
302impl Coerce<i64> for i32 {
303    #[inline(always)]
304    fn coerce(self) -> i64 {
305        self.into()
306    }
307}
308
309/// i32 -> isize: always safe (isize is at least 32 bits)
310impl Coerce<isize> for i32 {
311    #[inline(always)]
312    fn coerce(self) -> isize {
313        self as isize
314    }
315}
316
317/// i32 -> u32: can fail if negative
318impl TryCoerce<u32> for i32 {
319    type Error = CoerceError;
320
321    #[inline]
322    fn try_coerce(self) -> Result<u32, CoerceError> {
323        self.try_into().map_err(|_| CoerceError::Overflow)
324    }
325}
326
327/// i32 -> u64: can fail if negative
328impl TryCoerce<u64> for i32 {
329    type Error = CoerceError;
330
331    #[inline]
332    fn try_coerce(self) -> Result<u64, CoerceError> {
333        self.try_into().map_err(|_| CoerceError::Overflow)
334    }
335}
336
337/// i32 -> usize: can fail if negative
338impl TryCoerce<usize> for i32 {
339    type Error = CoerceError;
340
341    #[inline]
342    fn try_coerce(self) -> Result<usize, CoerceError> {
343        self.try_into().map_err(|_| CoerceError::Overflow)
344    }
345}
346// endregion
347
348// region: NonZero conversions (fallible - zero check)
349
350use core::num::{
351    NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroIsize, NonZeroU8, NonZeroU16, NonZeroU32,
352    NonZeroU64, NonZeroUsize,
353};
354
355macro_rules! impl_nonzero_from_self {
356    ($base:ty, $nz:ty) => {
357        impl TryCoerce<$nz> for $base {
358            type Error = CoerceError;
359
360            #[inline]
361            fn try_coerce(self) -> Result<$nz, CoerceError> {
362                <$nz>::new(self).ok_or(CoerceError::Zero)
363            }
364        }
365    };
366}
367
368// Direct NonZero conversions (same base type)
369impl TryCoerce<NonZeroI8> for i8 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroI8, CoerceError> {
        <NonZeroI8>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(i8, NonZeroI8);
370impl TryCoerce<NonZeroI16> for i16 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroI16, CoerceError> {
        <NonZeroI16>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(i16, NonZeroI16);
371impl TryCoerce<NonZeroI32> for i32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroI32, CoerceError> {
        <NonZeroI32>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(i32, NonZeroI32);
372impl TryCoerce<NonZeroI64> for i64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroI64, CoerceError> {
        <NonZeroI64>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(i64, NonZeroI64);
373impl TryCoerce<NonZeroIsize> for isize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroIsize, CoerceError> {
        <NonZeroIsize>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(isize, NonZeroIsize);
374impl TryCoerce<NonZeroU8> for u8 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroU8, CoerceError> {
        <NonZeroU8>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(u8, NonZeroU8);
375impl TryCoerce<NonZeroU16> for u16 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroU16, CoerceError> {
        <NonZeroU16>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(u16, NonZeroU16);
376impl TryCoerce<NonZeroU32> for u32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroU32, CoerceError> {
        <NonZeroU32>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(u32, NonZeroU32);
377impl TryCoerce<NonZeroU64> for u64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroU64, CoerceError> {
        <NonZeroU64>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(u64, NonZeroU64);
378impl TryCoerce<NonZeroUsize> for usize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<NonZeroUsize, CoerceError> {
        <NonZeroUsize>::new(self).ok_or(CoerceError::Zero)
    }
}impl_nonzero_from_self!(usize, NonZeroUsize);
379
380/// i32 -> NonZeroI64: widen then check zero
381impl TryCoerce<NonZeroI64> for i32 {
382    type Error = CoerceError;
383
384    #[inline]
385    fn try_coerce(self) -> Result<NonZeroI64, CoerceError> {
386        NonZeroI64::new(self.into()).ok_or(CoerceError::Zero)
387    }
388}
389
390/// i32 -> NonZeroIsize: widen then check zero
391impl TryCoerce<NonZeroIsize> for i32 {
392    type Error = CoerceError;
393
394    #[inline]
395    fn try_coerce(self) -> Result<NonZeroIsize, CoerceError> {
396        NonZeroIsize::new(self as isize).ok_or(CoerceError::Zero)
397    }
398}
399
400/// i32 -> NonZeroU32: check non-negative and non-zero
401impl TryCoerce<NonZeroU32> for i32 {
402    type Error = CoerceError;
403
404    #[inline]
405    fn try_coerce(self) -> Result<NonZeroU32, CoerceError> {
406        let u: u32 = self.try_into().map_err(|_| CoerceError::Overflow)?;
407        NonZeroU32::new(u).ok_or(CoerceError::Zero)
408    }
409}
410
411/// i32 -> NonZeroU64: check non-negative and non-zero
412impl TryCoerce<NonZeroU64> for i32 {
413    type Error = CoerceError;
414
415    #[inline]
416    fn try_coerce(self) -> Result<NonZeroU64, CoerceError> {
417        let u: u64 = self.try_into().map_err(|_| CoerceError::Overflow)?;
418        NonZeroU64::new(u).ok_or(CoerceError::Zero)
419    }
420}
421
422/// i32 -> NonZeroUsize: check non-negative and non-zero
423impl TryCoerce<NonZeroUsize> for i32 {
424    type Error = CoerceError;
425
426    #[inline]
427    fn try_coerce(self) -> Result<NonZeroUsize, CoerceError> {
428        let u: usize = self.try_into().map_err(|_| CoerceError::Overflow)?;
429        NonZeroUsize::new(u).ok_or(CoerceError::Zero)
430    }
431}
432
433/// i32 -> NonZeroI8: narrow then check zero
434impl TryCoerce<NonZeroI8> for i32 {
435    type Error = CoerceError;
436
437    #[inline]
438    fn try_coerce(self) -> Result<NonZeroI8, CoerceError> {
439        let n: i8 = self.try_into().map_err(|_| CoerceError::Overflow)?;
440        NonZeroI8::new(n).ok_or(CoerceError::Zero)
441    }
442}
443
444/// i32 -> NonZeroI16: narrow then check zero
445impl TryCoerce<NonZeroI16> for i32 {
446    type Error = CoerceError;
447
448    #[inline]
449    fn try_coerce(self) -> Result<NonZeroI16, CoerceError> {
450        let n: i16 = self.try_into().map_err(|_| CoerceError::Overflow)?;
451        NonZeroI16::new(n).ok_or(CoerceError::Zero)
452    }
453}
454
455/// i32 -> NonZeroU8: check non-negative, narrow, then check zero
456impl TryCoerce<NonZeroU8> for i32 {
457    type Error = CoerceError;
458
459    #[inline]
460    fn try_coerce(self) -> Result<NonZeroU8, CoerceError> {
461        let u: u8 = self.try_into().map_err(|_| CoerceError::Overflow)?;
462        NonZeroU8::new(u).ok_or(CoerceError::Zero)
463    }
464}
465
466/// i32 -> NonZeroU16: check non-negative, narrow, then check zero
467impl TryCoerce<NonZeroU16> for i32 {
468    type Error = CoerceError;
469
470    #[inline]
471    fn try_coerce(self) -> Result<NonZeroU16, CoerceError> {
472        let u: u16 = self.try_into().map_err(|_| CoerceError::Overflow)?;
473        NonZeroU16::new(u).ok_or(CoerceError::Zero)
474    }
475}
476// endregion
477
478// region: i32/Rboolean to bool (fallible - NA handling)
479
480/// Error type for logical coercion failures.
481#[derive(#[automatically_derived]
impl ::core::fmt::Debug for LogicalCoerceError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::NAValue => ::core::fmt::Formatter::write_str(f, "NAValue"),
            Self::InvalidValue(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "InvalidValue", &__self_0),
        }
    }
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for LogicalCoerceError { }
#[automatically_derived]
impl ::core::clone::Clone for LogicalCoerceError {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<i32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LogicalCoerceError { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for LogicalCoerceError { }
#[automatically_derived]
impl ::core::cmp::PartialEq for LogicalCoerceError {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::InvalidValue(__self_0), Self::InvalidValue(__arg1_0))
                    => __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for LogicalCoerceError {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<i32>;
    }
}Eq)]
482pub enum LogicalCoerceError {
483    /// R's NA_LOGICAL cannot be represented as Rust bool
484    NAValue,
485    /// Value is not 0 or 1
486    InvalidValue(i32),
487}
488
489impl std::fmt::Display for LogicalCoerceError {
490    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
491        match self {
492            LogicalCoerceError::NAValue => f.write_fmt(format_args!("NA is not allowed"))write!(f, "NA is not allowed"),
493            LogicalCoerceError::InvalidValue(v) => f.write_fmt(format_args!("{0} is neither 0 nor 1", v))write!(f, "{v} is neither 0 nor 1"),
494        }
495    }
496}
497
498impl std::error::Error for LogicalCoerceError {}
499
500impl TryCoerce<bool> for i32 {
501    type Error = LogicalCoerceError;
502
503    #[inline]
504    fn try_coerce(self) -> Result<bool, LogicalCoerceError> {
505        match self {
506            0 => Ok(false),
507            1 => Ok(true),
508            // NA_LOGICAL is i32::MIN in R
509            v if v.is_na() => Err(LogicalCoerceError::NAValue),
510            other => Err(LogicalCoerceError::InvalidValue(other)),
511        }
512    }
513}
514
515impl TryCoerce<bool> for Rboolean {
516    type Error = LogicalCoerceError;
517
518    #[inline]
519    fn try_coerce(self) -> Result<bool, LogicalCoerceError> {
520        (self as i32).try_coerce()
521    }
522}
523
524impl TryCoerce<bool> for crate::RLogical {
525    type Error = LogicalCoerceError;
526
527    #[inline]
528    fn try_coerce(self) -> Result<bool, LogicalCoerceError> {
529        self.to_i32().try_coerce()
530    }
531}
532// endregion
533
534// region: Narrowing to i32 (fallible)
535
536impl TryCoerce<i32> for u32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i32, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i32> for u64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i32, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i32> for usize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i32, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i32> for i64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i32, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i32> for isize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i32, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}impl_try_narrow!(i32; u32, u64, usize, i64, isize);
537// endregion
538
539// region: Narrowing to u8 (fallible)
540
541impl TryCoerce<u8> for i8 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u8> for i16 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u8> for i32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u8> for i64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u8> for u16 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u8> for u32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u8> for u64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u8> for usize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u8> for isize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}impl_try_narrow!(u8; i8, i16, i32, i64, u16, u32, u64, usize, isize);
542// endregion
543
544// region: Widening to u16/i16/u32 (infallible)
545
546impl Coerce<u16> for u8 {
    #[inline(always)]
    fn coerce(self) -> u16 { self.into() }
}
impl Coerce<i16> for i8 {
    #[inline(always)]
    fn coerce(self) -> i16 { self.into() }
}
impl Coerce<i16> for u8 {
    #[inline(always)]
    fn coerce(self) -> i16 { self.into() }
}
impl Coerce<u32> for u8 {
    #[inline(always)]
    fn coerce(self) -> u32 { self.into() }
}
impl Coerce<u32> for u16 {
    #[inline(always)]
    fn coerce(self) -> u32 { self.into() }
}impl_widen!(u8 => u16, i8 => i16, u8 => i16, u8 => u32, u16 => u32);
547// endregion
548
549// region: Narrowing to u16 (fallible)
550
551impl TryCoerce<u16> for i8 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u16> for i16 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u16> for i32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u16> for i64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u16> for u32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u16> for u64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u16> for usize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<u16> for isize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<u16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}impl_try_narrow!(u16; i8, i16, i32, i64, u32, u64, usize, isize);
552// endregion
553
554// region: Narrowing to i16 (fallible)
555
556impl TryCoerce<i16> for i32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i16> for i64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i16> for u16 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i16> for u32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i16> for u64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i16> for usize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i16> for isize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i16, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}impl_try_narrow!(i16; i32, i64, u16, u32, u64, usize, isize);
557// endregion
558
559// region: Narrowing to i8 (fallible)
560
561impl TryCoerce<i8> for i16 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i8> for i32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i8> for i64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i8> for u8 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i8> for u16 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i8> for u32 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i8> for u64 {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i8> for usize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}
impl TryCoerce<i8> for isize {
    type Error = CoerceError;
    #[inline]
    fn try_coerce(self) -> Result<i8, CoerceError> {
        self.try_into().map_err(|_| CoerceError::Overflow)
    }
}impl_try_narrow!(i8; i16, i32, i64, u8, u16, u32, u64, usize, isize);
562// endregion
563
564// region: Float to smaller integers (fallible)
565
566impl TryCoerce<u16> for f64 {
567    type Error = CoerceError;
568
569    #[inline]
570    fn try_coerce(self) -> Result<u16, CoerceError> {
571        if self.is_nan() {
572            return Err(CoerceError::NaN);
573        }
574        if self.is_infinite() {
575            return Err(CoerceError::Overflow);
576        }
577        if self < 0.0 || self > u16::MAX as f64 {
578            return Err(CoerceError::Overflow);
579        }
580        if self.fract() != 0.0 {
581            return Err(CoerceError::PrecisionLoss);
582        }
583        Ok(self as u16)
584    }
585}
586
587impl TryCoerce<i16> for f64 {
588    type Error = CoerceError;
589
590    #[inline]
591    fn try_coerce(self) -> Result<i16, CoerceError> {
592        if self.is_nan() {
593            return Err(CoerceError::NaN);
594        }
595        if self.is_infinite() {
596            return Err(CoerceError::Overflow);
597        }
598        if self < i16::MIN as f64 || self > i16::MAX as f64 {
599            return Err(CoerceError::Overflow);
600        }
601        if self.fract() != 0.0 {
602            return Err(CoerceError::PrecisionLoss);
603        }
604        Ok(self as i16)
605    }
606}
607
608impl TryCoerce<i8> for f64 {
609    type Error = CoerceError;
610
611    #[inline]
612    fn try_coerce(self) -> Result<i8, CoerceError> {
613        if self.is_nan() {
614            return Err(CoerceError::NaN);
615        }
616        if self.is_infinite() {
617            return Err(CoerceError::Overflow);
618        }
619        if self < i8::MIN as f64 || self > i8::MAX as f64 {
620            return Err(CoerceError::Overflow);
621        }
622        if self.fract() != 0.0 {
623            return Err(CoerceError::PrecisionLoss);
624        }
625        Ok(self as i8)
626    }
627}
628// endregion
629
630// region: Float to i32 (fallible)
631
632/// R's integer range is `(i32::MIN, i32::MAX]`: `i32::MIN` is the
633/// `NA_integer_` sentinel, so `-2147483648.0` is out of range rather than a
634/// value that would reach R as `NA` (`as.integer(-2^31)` is `NA` too).
635impl TryCoerce<i32> for f64 {
636    type Error = CoerceError;
637
638    #[inline]
639    fn try_coerce(self) -> Result<i32, CoerceError> {
640        if self.is_nan() {
641            return Err(CoerceError::NaN);
642        }
643        if self.is_infinite() {
644            return Err(CoerceError::Overflow);
645        }
646        if self <= i32::MIN as f64 || self > i32::MAX as f64 {
647            return Err(CoerceError::Overflow);
648        }
649        if self.fract() != 0.0 {
650            return Err(CoerceError::PrecisionLoss);
651        }
652        Ok(self as i32)
653    }
654}
655
656impl TryCoerce<i32> for f32 {
657    type Error = CoerceError;
658
659    #[inline]
660    fn try_coerce(self) -> Result<i32, CoerceError> {
661        f64::from(self).try_coerce()
662    }
663}
664
665// f64 → f32 narrowing (always succeeds, may lose precision or become inf)
666impl Coerce<f32> for f64 {
667    #[inline(always)]
668    fn coerce(self) -> f32 {
669        self as f32
670    }
671}
672// endregion
673
674// region: Float to u8 (fallible) - for RAWSXP
675
676impl TryCoerce<u8> for f64 {
677    type Error = CoerceError;
678
679    #[inline]
680    fn try_coerce(self) -> Result<u8, CoerceError> {
681        if self.is_nan() {
682            return Err(CoerceError::NaN);
683        }
684        if self.is_infinite() {
685            return Err(CoerceError::Overflow);
686        }
687        if self < 0.0 || self > u8::MAX as f64 {
688            return Err(CoerceError::Overflow);
689        }
690        if self.fract() != 0.0 {
691            return Err(CoerceError::PrecisionLoss);
692        }
693        Ok(self as u8)
694    }
695}
696
697impl TryCoerce<u8> for f32 {
698    type Error = CoerceError;
699
700    #[inline]
701    fn try_coerce(self) -> Result<u8, CoerceError> {
702        f64::from(self).try_coerce()
703    }
704}
705// endregion
706
707// region: Float to u32 (fallible)
708
709impl TryCoerce<u32> for f64 {
710    type Error = CoerceError;
711
712    #[inline]
713    fn try_coerce(self) -> Result<u32, CoerceError> {
714        if self.is_nan() {
715            return Err(CoerceError::NaN);
716        }
717        if self.is_infinite() {
718            return Err(CoerceError::Overflow);
719        }
720        if self < 0.0 || self > u32::MAX as f64 {
721            return Err(CoerceError::Overflow);
722        }
723        if self.fract() != 0.0 {
724            return Err(CoerceError::PrecisionLoss);
725        }
726        Ok(self as u32)
727    }
728}
729// endregion
730
731// region: Float to i64/u64 (fallible)
732//
733// These conversions validate that the f64 can be exactly represented as an integer.
734//
735// **Checks performed:**
736// - NaN → `Err(CoerceError::NaN)`
737// - Infinity → `Err(CoerceError::Overflow)`
738// - Out of range → `Err(CoerceError::Overflow)`
739// - Has fractional part → `Err(CoerceError::PrecisionLoss)`
740//
741// **Note on precision:**
742// f64 can exactly represent all integers in [-2^53, 2^53]. For values in this
743// range that pass the fractional check, conversion is exact. For larger f64
744// values (which must have been created through approximation), the conversion
745// returns whatever integer the f64 represents, which may not be what was
746// originally intended.
747
748/// Convert `f64` to `i64`, validating exact representation.
749impl TryCoerce<i64> for f64 {
750    type Error = CoerceError;
751
752    #[inline]
753    fn try_coerce(self) -> Result<i64, CoerceError> {
754        if self.is_nan() {
755            return Err(CoerceError::NaN);
756        }
757        if self.is_infinite() {
758            return Err(CoerceError::Overflow);
759        }
760        // i64::MIN/MAX can't be exactly represented in f64, so use safe bounds
761        if self < i64::MIN as f64 || self >= i64::MAX as f64 {
762            return Err(CoerceError::Overflow);
763        }
764        if self.fract() != 0.0 {
765            return Err(CoerceError::PrecisionLoss);
766        }
767        Ok(self as i64)
768    }
769}
770
771impl TryCoerce<u64> for f64 {
772    type Error = CoerceError;
773
774    #[inline]
775    fn try_coerce(self) -> Result<u64, CoerceError> {
776        if self.is_nan() {
777            return Err(CoerceError::NaN);
778        }
779        if self.is_infinite() {
780            return Err(CoerceError::Overflow);
781        }
782        if self < 0.0 || self >= u64::MAX as f64 {
783            return Err(CoerceError::Overflow);
784        }
785        if self.fract() != 0.0 {
786            return Err(CoerceError::PrecisionLoss);
787        }
788        Ok(self as u64)
789    }
790}
791
792impl TryCoerce<isize> for f64 {
793    type Error = CoerceError;
794
795    #[inline]
796    fn try_coerce(self) -> Result<isize, CoerceError> {
797        if self.is_nan() {
798            return Err(CoerceError::NaN);
799        }
800        if self.is_infinite() {
801            return Err(CoerceError::Overflow);
802        }
803        // Upper check uses >= because isize::MAX as f64 rounds up on 64-bit
804        if self < isize::MIN as f64 || self >= isize::MAX as f64 {
805            return Err(CoerceError::Overflow);
806        }
807        if self.fract() != 0.0 {
808            return Err(CoerceError::PrecisionLoss);
809        }
810        Ok(self as isize)
811    }
812}
813
814impl TryCoerce<usize> for f64 {
815    type Error = CoerceError;
816
817    #[inline]
818    fn try_coerce(self) -> Result<usize, CoerceError> {
819        if self.is_nan() {
820            return Err(CoerceError::NaN);
821        }
822        if self.is_infinite() {
823            return Err(CoerceError::Overflow);
824        }
825        // Upper check uses >= because usize::MAX as f64 rounds up on 64-bit
826        if self < 0.0 || self >= usize::MAX as f64 {
827            return Err(CoerceError::Overflow);
828        }
829        if self.fract() != 0.0 {
830            return Err(CoerceError::PrecisionLoss);
831        }
832        Ok(self as usize)
833    }
834}
835// endregion
836
837// region: Large int to f64 (fallible - precision)
838//
839// These conversions only succeed if the integer can be exactly represented
840// in f64. This is stricter than Rust's `as f64` which silently rounds.
841//
842// **Safe integer range:**
843// - f64 has 53 bits of mantissa precision
844// - Integers in [-2^53, 2^53] (±9,007,199,254,740,992) are exactly representable
845// - Outside this range: `Err(CoerceError::PrecisionLoss)`
846//
847// **Use cases:**
848// - Validating R function inputs won't lose precision
849// - Checked conversions in data pipelines
850// - Ensuring round-trip fidelity (i64 → R → i64)
851
852/// Convert `i64` to `f64`, failing if precision would be lost.
853///
854/// Only succeeds for values in [-2^53, 2^53].
855impl TryCoerce<f64> for i64 {
856    type Error = CoerceError;
857
858    #[inline]
859    fn try_coerce(self) -> Result<f64, CoerceError> {
860        const MAX_SAFE: i64 = 1 << 53;
861        const MIN_SAFE: i64 = -(1 << 53);
862        if !(MIN_SAFE..=MAX_SAFE).contains(&self) {
863            return Err(CoerceError::PrecisionLoss);
864        }
865        Ok(self as f64)
866    }
867}
868
869/// Convert `u64` to `f64`, failing if precision would be lost.
870///
871/// Only succeeds for values ≤ 2^53.
872impl TryCoerce<f64> for u64 {
873    type Error = CoerceError;
874
875    #[inline]
876    fn try_coerce(self) -> Result<f64, CoerceError> {
877        const MAX_SAFE: u64 = 1 << 53;
878        if self > MAX_SAFE {
879            return Err(CoerceError::PrecisionLoss);
880        }
881        Ok(self as f64)
882    }
883}
884
885impl TryCoerce<f64> for isize {
886    type Error = CoerceError;
887    #[inline]
888    fn try_coerce(self) -> Result<f64, CoerceError> {
889        (self as i64).try_coerce()
890    }
891}
892
893impl TryCoerce<f64> for usize {
894    type Error = CoerceError;
895    #[inline]
896    fn try_coerce(self) -> Result<f64, CoerceError> {
897        (self as u64).try_coerce()
898    }
899}
900// endregion
901
902// region: Coerced wrapper type
903
904use std::marker::PhantomData;
905
906/// Wrapper for values coerced from an R native type during conversion.
907///
908/// This enables using non-native Rust types in collections read from R:
909///
910/// ```ignore
911/// // Read a Vec of i64 from R integers (i32)
912/// let vec: Vec<Coerced<i64, i32>> = TryFromSexp::try_from_sexp(sexp)?;
913///
914/// // Extract the values
915/// let i64_vec: Vec<i64> = vec.into_iter().map(Coerced::into_inner).collect();
916/// ```
917///
918/// The type parameters are:
919/// - `T`: The target Rust type you want
920/// - `R`: The R-native type to read and coerce from
921#[repr(transparent)]
922#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug, R: ::core::fmt::Debug> ::core::fmt::Debug for
    Coerced<T, R> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "Coerced",
            "value", &self.value, "_marker", &&self._marker)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone, R: ::core::clone::Clone> ::core::clone::Clone
    for Coerced<T, R> {
    #[inline]
    fn clone(&self) -> Self {
        Coerced {
            value: ::core::clone::Clone::clone(&self.value),
            _marker: ::core::clone::Clone::clone(&self._marker),
        }
    }
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy, R: ::core::marker::Copy> ::core::marker::Copy
    for Coerced<T, R> {
}Copy, #[automatically_derived]
impl<T: ::core::cmp::PartialEq, R: ::core::cmp::PartialEq>
    ::core::marker::StructuralPartialEq for Coerced<T, R> {
}
#[automatically_derived]
impl<T: ::core::cmp::PartialEq, R: ::core::cmp::PartialEq>
    ::core::cmp::PartialEq for Coerced<T, R> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value && self._marker == other._marker
    }
}PartialEq, #[automatically_derived]
impl<T: ::core::cmp::Eq, R: ::core::cmp::Eq> ::core::cmp::Eq for Coerced<T, R>
    {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<T>;
        let _: ::core::cmp::AssertParamIsEq<PhantomData<R>>;
    }
}Eq, #[automatically_derived]
impl<T: ::core::cmp::PartialOrd, R: ::core::cmp::PartialOrd>
    ::core::cmp::PartialOrd for Coerced<T, R> {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        match ::core::cmp::PartialOrd::partial_cmp(&self.value, &other.value)
            {
            ::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
                ::core::cmp::PartialOrd::partial_cmp(&self._marker,
                    &other._marker),
            cmp => cmp,
        }
    }
}PartialOrd, #[automatically_derived]
impl<T: ::core::cmp::Ord, R: ::core::cmp::Ord> ::core::cmp::Ord for
    Coerced<T, R> {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        match ::core::cmp::Ord::cmp(&self.value, &other.value) {
            ::core::cmp::Ordering::Equal =>
                ::core::cmp::Ord::cmp(&self._marker, &other._marker),
            cmp => cmp,
        }
    }
}Ord, #[automatically_derived]
impl<T: ::core::hash::Hash, R: ::core::hash::Hash> ::core::hash::Hash for
    Coerced<T, R> {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.value, state);
        ::core::hash::Hash::hash(&self._marker, state)
    }
}Hash, #[automatically_derived]
impl<T: ::core::default::Default, R: ::core::default::Default>
    ::core::default::Default for Coerced<T, R> {
    #[inline]
    fn default() -> Self {
        Coerced {
            value: ::core::default::Default::default(),
            _marker: ::core::default::Default::default(),
        }
    }
}Default)]
923pub struct Coerced<T, R> {
924    value: T,
925    _marker: PhantomData<R>,
926}
927
928impl<T, R> Coerced<T, R> {
929    /// Create a new Coerced wrapper.
930    #[inline]
931    pub const fn new(value: T) -> Self {
932        Self {
933            value,
934            _marker: PhantomData,
935        }
936    }
937
938    /// Extract the inner value.
939    #[inline]
940    pub fn into_inner(self) -> T {
941        self.value
942    }
943
944    /// Get a reference to the inner value.
945    #[inline]
946    pub const fn as_inner(&self) -> &T {
947        &self.value
948    }
949
950    /// Get a mutable reference to the inner value.
951    #[inline]
952    pub fn as_inner_mut(&mut self) -> &mut T {
953        &mut self.value
954    }
955}
956
957impl<T, R> std::ops::Deref for Coerced<T, R> {
958    type Target = T;
959
960    #[inline]
961    fn deref(&self) -> &Self::Target {
962        &self.value
963    }
964}
965
966impl<T, R> std::ops::DerefMut for Coerced<T, R> {
967    #[inline]
968    fn deref_mut(&mut self) -> &mut Self::Target {
969        &mut self.value
970    }
971}
972// endregion
973
974// region: Slice coercions (element-wise)
975
976/// Coerce a slice element-wise to a Vec.
977impl<T: Copy + Coerce<R>, R> Coerce<Vec<R>> for &[T] {
978    #[inline]
979    fn coerce(self) -> Vec<R> {
980        self.iter().copied().map(Coerce::coerce).collect()
981    }
982}
983
984/// Coerce a Vec element-wise to a new Vec.
985impl<T: Coerce<R>, R> Coerce<Vec<R>> for Vec<T> {
986    #[inline]
987    fn coerce(self) -> Vec<R> {
988        self.into_iter().map(Coerce::coerce).collect()
989    }
990}
991
992/// Infallible element-wise coercion for `Box<[T]>` → `Box<[R]>`.
993impl<T: Coerce<R>, R> Coerce<Box<[R]>> for Box<[T]> {
994    #[inline]
995    fn coerce(self) -> Box<[R]> {
996        Vec::from(self).into_iter().map(Coerce::coerce).collect()
997    }
998}
999
1000/// Infallible element-wise coercion for VecDeque to VecDeque.
1001impl<T: Coerce<R>, R> Coerce<std::collections::VecDeque<R>> for std::collections::VecDeque<T> {
1002    fn coerce(self) -> std::collections::VecDeque<R> {
1003        self.into_iter().map(Coerce::coerce).collect()
1004    }
1005}
1006
1007// Note: TryCoerce<Vec<R>> is automatically provided by the blanket impl
1008// `impl<T: Coerce<R>> TryCoerce<R> for T`. For types that only implement
1009// TryCoerce (not Coerce), use manual iteration:
1010// slice.iter().map(|x| x.try_coerce()).collect::<Result<Vec<_>, _>>()
1011// endregion
1012
1013// region: TinyVec coercions (element-wise)
1014
1015#[cfg(feature = "tinyvec")]
1016/// Element-wise coercion for TinyVec.
1017///
1018/// Enables conversions like `TinyVec<[i8; 10]>` → `TinyVec<[i32; 10]>` via widening.
1019impl<T, R, const N: usize> Coerce<tinyvec::TinyVec<[R; N]>> for tinyvec::TinyVec<[T; N]>
1020where
1021    T: Coerce<R>,
1022    [T; N]: tinyvec::Array<Item = T>,
1023    [R; N]: tinyvec::Array<Item = R>,
1024{
1025    fn coerce(self) -> tinyvec::TinyVec<[R; N]> {
1026        self.into_iter().map(Coerce::coerce).collect()
1027    }
1028}
1029
1030#[cfg(feature = "tinyvec")]
1031/// Element-wise coercion for ArrayVec.
1032///
1033/// Enables conversions like `ArrayVec<[i8; 10]>` → `ArrayVec<[i32; 10]>` via widening.
1034impl<T, R, const N: usize> Coerce<tinyvec::ArrayVec<[R; N]>> for tinyvec::ArrayVec<[T; N]>
1035where
1036    T: Coerce<R>,
1037    [T; N]: tinyvec::Array<Item = T>,
1038    [R; N]: tinyvec::Array<Item = R>,
1039{
1040    fn coerce(self) -> tinyvec::ArrayVec<[R; N]> {
1041        self.into_iter().map(Coerce::coerce).collect()
1042    }
1043}
1044// endregion
1045
1046// region: Tuple coercions (element-wise)
1047
1048/// Macro to implement element-wise Coerce for tuples.
1049macro_rules! impl_tuple_coerce {
1050    ($n:literal, $name:literal; $($T:ident $R:ident $idx:tt),+) => {
1051        impl<$($T,)+ $($R,)+> Coerce<($($R,)+)> for ($($T,)+)
1052        where
1053            $($T: Coerce<$R>,)+
1054        {
1055            #[inline]
1056            fn coerce(self) -> ($($R,)+) {
1057                ($(Coerce::<$R>::coerce(self.$idx),)+)
1058            }
1059        }
1060    };
1061}
1062
1063impl<A, B, C, D, E, F, G, H, I, J, K, L, RA, RB, RC, RD, RE, RF, RG, RH, RI,
    RJ, RK, RL> Coerce<(RA, RB, RC, RD, RE, RF, RG, RH, RI, RJ, RK, RL)> for
    (A, B, C, D, E, F, G, H, I, J, K, L) where A: Coerce<RA>, B: Coerce<RB>,
    C: Coerce<RC>, D: Coerce<RD>, E: Coerce<RE>, F: Coerce<RF>, G: Coerce<RG>,
    H: Coerce<RH>, I: Coerce<RI>, J: Coerce<RJ>, K: Coerce<RK>, L: Coerce<RL>
    {
    #[inline]
    fn coerce(self) -> (RA, RB, RC, RD, RE, RF, RG, RH, RI, RJ, RK, RL) {
        (Coerce::<RA>::coerce(self.0), Coerce::<RB>::coerce(self.1),
            Coerce::<RC>::coerce(self.2), Coerce::<RD>::coerce(self.3),
            Coerce::<RE>::coerce(self.4), Coerce::<RF>::coerce(self.5),
            Coerce::<RG>::coerce(self.6), Coerce::<RH>::coerce(self.7),
            Coerce::<RI>::coerce(self.8), Coerce::<RJ>::coerce(self.9),
            Coerce::<RK>::coerce(self.10), Coerce::<RL>::coerce(self.11))
    }
}crate::tuple_macros::for_each_tuple!(impl_tuple_coerce);
1064// endregion
1065
1066// region: Tests
1067
1068#[cfg(test)]
1069mod tests;
1070// endregion