Skip to main content

miniextendr_api/
newtype.rs

1//! Container conversions for newtypes and string-parsed types.
2//!
3//! `#[derive(TryFromSexp)]` / `#[derive(IntoR)]` on a single-field newtype
4//! (`struct UserId(Uuid)`) emit the scalar forwarding impls *and* a small marker
5//! impl from this module. The container blankets here then light up
6//! `Vec<UserId>`, `Option<UserId>`, and `Vec<Option<UserId>>` automatically — the
7//! newtype inherits the inner type's exact SEXPTYPE checks, NA policy, error
8//! text and `TryFromSexp::CHARACTER_ONLY` in every shape.
9//!
10//! A type parsed from an R string gets the same three shapes from
11//! [`try_from_sexp_via_str_parse!`](crate::try_from_sexp_via_str_parse), which
12//! implements [`TryFromSexpElement`] with
13//! [`ParsedRStr`](crate::from_r::ParsedRStr) as the inner type (#1766).
14//!
15//! # The containers forward; a check refuses
16//!
17//! The containers never call the element's own `TryFromSexp::try_from_sexp`.
18//! `Option<T>` reads `Option<T::Inner>`, `Vec<T>` reads `Vec<T::Inner>` and
19//! `Vec<Option<T>>` reads `Vec<Option<T::Inner>>`, each wrapping what it
20//! read with [`TryFromSexpElement::from_inner`]. So an element's scalar
21//! `try_from_sexp` must be the inner type's followed by `from_inner`, which
22//! is what the derive emits, and refusing values a type's inner type
23//! accepts belongs in [`TryFromSexpElement::check_sexp`], not in
24//! `try_from_sexp`. The check runs on the R value before it is read, in the
25//! scalar `try_from_sexp`, on a non-`NULL` input for `Option<T>` (`NULL` is
26//! "not given"), and for `Vec<T>` / `Vec<Option<T>>` where R keeps the
27//! element's class. `#[derive(TryFromSexp)]` takes it as
28//! `#[try_from_sexp(validate = path)]` (#1815).
29//!
30//! # Where a `Vec`'s check runs: the inner container decides
31//!
32//! An atomic vector keeps one class for all its elements, on the vector; a
33//! list of R objects keeps a class on each element. So the `Vec` blankets
34//! don't run the check themselves: they pass `T::check_sexp` to the inner
35//! container's hidden
36//! [`TryFromSexp::__mx_try_from_sexp_with_check`]
37//! (#1837). Its default runs the check once on the whole input, which every
38//! atomic inner container keeps. `Vec<List>` and `Vec<Option<List>>` override
39//! it to run the check on each element (but `NULL`) and to report every
40//! failing element in one error, with its position. The blankets override it
41//! too, adding their own check after the one they are given, so a newtype of a
42//! newtype over `List` checks each element, outer check first, as its scalar
43//! does.
44//!
45//! This keeps one blanket per container slot and one element trait: the
46//! choice follows the inner type, through an impl that only `miniextendr-api`
47//! can write (`Vec<List>`), not a second marker trait a downstream type could
48//! implement next to `TryFromSexpElement`. The `Option<T>` blanket runs the
49//! check itself: a scalar keeps its class on itself whatever its inner type.
50//!
51//! # Why the markers live here and not in the derive
52//!
53//! A downstream crate cannot write `impl TryFromSexp for Vec<MyNewtype>`: `Vec` /
54//! `Option` are not `#[fundamental]`, so the local newtype is "covered" and the
55//! orphan rule (E0117) forbids it. The container impls must live in
56//! `miniextendr-api`, keyed on a marker trait the derive *can* legally implement
57//! downstream (foreign trait + local `Self`). The blankets below are those
58//! container impls (#844).
59//!
60//! [`TryFromSexpElement`] / [`IntoRNewtype`] / [`IntoRVecElement`] are
61//! **plumbing**: they are emitted by the derives and by
62//! `try_from_sexp_via_str_parse!`, not implemented by hand. Implementing them
63//! manually is supported but unusual. A hand-written [`TryFromSexpElement`]
64//! also needs a `TryFromSexp` impl on the type for `Option<T>` to convert (the
65//! derive and the macro emit both), and that impl must keep the forwarding
66//! rule above: call [`TryFromSexpElement::check_sexp`], then convert the inner
67//! type and wrap it, and nothing else, since no container ever runs it.
68//!
69//! # One element trait per direction
70//!
71//! Each `TryFromSexp` container slot (`Vec<T>`, `Option<T>`, `Vec<Option<T>>`)
72//! holds exactly one blanket, keyed on [`TryFromSexpElement`]. A second blanket
73//! on another public trait (say, a parse trait) would collide with it (E0119):
74//! a downstream type could implement both, so coherence cannot prove them
75//! disjoint. So every non-built-in element goes through this one trait, and
76//! what differs is the inner type the containers read first: the newtype's
77//! field type, or `ParsedRStr<T>` for a string-parsed type, whose container
78//! impls in `from_r` hold the NA policy and the batched element errors.
79//!
80//! Every container's error is [`SexpError`]: the inner container's error
81//! converts into it, and so does a check's refusal
82//! ([`SexpError::Condition`], carrying
83//! its own classes). The inner containers that exist are all `miniextendr-api`
84//! impls (the orphan rule keeps the rest out), and each one's error is a
85//! `SexpError` or converts into one.
86//!
87//! The blankets bound the inner type through the hidden `VecInner` /
88//! `OptionInner` / `VecOptionInner` helpers rather than
89//! `Vec<T::Inner>: TryFromSexp`. For a type that is not an element,
90//! `T::Inner` cannot be normalized; with the projection as the bound's self
91//! type, the solver gives up on it at once and reports
92//! `T: TryFromSexpElement` as unsatisfied (E0277). Spelled
93//! `Vec<T::Inner>: TryFromSexp`, the same bound sent the solver through
94//! every `Vec<_>` impl (`Vec<Vec<_>>` nests one level per step) until it
95//! overflowed (E0275, #1682).
96//!
97//! # The asymmetries
98//!
99//! Five of the six container shapes are granted. Two are not, for two different
100//! coherence reasons:
101//!
102//! - **`IntoR for Vec<T>` (granted, but shared).** This slot has exactly one
103//!   blanket and `MatchArg` already needs it (`Vec<MyEnum>` → STRSXP). Rather
104//!   than a second, conflicting `Vec<T>` blanket (E0119), both paths funnel
105//!   through [`IntoRVecElement`]: `MatchArg` types reach it via a bridge blanket
106//!   in `match_arg.rs`, newtypes via a concrete impl emitted by
107//!   `#[derive(IntoR)]`. A type that is *both* a `MatchArg` enum and an `IntoR`
108//!   newtype is a coherence error — don't derive both on one type.
109//! - **`IntoR for Option<T>` (not granted).** A bare `Option<T>` blanket
110//!   collides with the pre-existing `impl<T: Copy + IntoR> IntoR for Option<&T>`:
111//!   `&T` is `#[fundamental]`, so a downstream crate could impl `IntoRNewtype`
112//!   for `&LocalType` and coherence cannot prove the two disjoint. Return
113//!   `Option<Inner>` (`opt.map(|x| x.0)`) instead. See the note on the missing
114//!   blanket below.
115//!
116//! `TryFromSexp for Vec<T>` / `Option<T>` / `Vec<Option<T>>` and `IntoR for
117//! Vec<Option<T>>` are coherence-free: no other blanket occupies those slots.
118//! The concrete `TryFromSexp for Vec<List>` / `Vec<Option<List>>` impls sit
119//! beside them because `List` is not a `TryFromSexpElement`, and no other
120//! crate can make it one (both are `miniextendr-api`'s).
121
122use crate::from_r::{ElementCheck, NativeBorrow, SexpError, TryFromSexp, no_element_check};
123use crate::into_r::IntoR;
124use crate::{SEXP, SEXPTYPE, SexpExt};
125
126// region: marker traits (emitted by the derives, not hand-written)
127
128/// The element side of the `TryFromSexp` impls for `Vec<T>`, `Option<T>` and
129/// `Vec<Option<T>>` (R → Rust).
130///
131/// Each container of `Self` is read as the same container of
132/// [`Inner`](Self::Inner), then every element is wrapped with
133/// [`from_inner`](Self::from_inner): `Vec<Self>` reads `Vec<Self::Inner>`,
134/// `Option<Self>` reads `Option<Self::Inner>`, and `Vec<Option<Self>>` reads
135/// `Vec<Option<Self::Inner>>`. Each shape exists when the inner container
136/// converts, independently of the other two, and keeps its NA policy and
137/// metadata (`NATIVE_BORROW`, `CHARACTER_ONLY`); its error converts into
138/// [`SexpError`].
139///
140/// # Forwarding
141///
142/// No container calls `<Self as TryFromSexp>::try_from_sexp`. They read the
143/// inner type and wrap it, so the element's own `try_from_sexp`, when it has
144/// one, must do the same: [`check_sexp`](Self::check_sexp), then the inner
145/// type's conversion, then [`from_inner`](Self::from_inner). Anything else
146/// it did would hold for a scalar argument and silently not for
147/// `Option<Self>`, `Vec<Self>` or `Vec<Option<Self>>`. A value the inner type
148/// accepts but `Self` refuses is refused in `check_sexp`, which every shape
149/// runs.
150///
151/// Emitted by `#[derive(TryFromSexp)]` (the inner type is the newtype's field;
152/// `#[try_from_sexp(validate = path)]` gives the check) and by
153/// [`try_from_sexp_via_str_parse!`](crate::try_from_sexp_via_str_parse)
154/// (the inner type is [`ParsedRStr<Self>`](crate::from_r::ParsedRStr)). See
155/// the module docs for why both share this one trait.
156#[diagnostic::on_unimplemented(
157    message = "this `Vec` or `Option` of `{Self}` has no conversion from R",
158    label = "`{Self}` does not implement `TryFromSexpElement`",
159    note = "`Vec<T>`, `Option<T>` and `Vec<Option<T>>` convert from R for the built-in types, `#[derive(TryFromSexp)]` newtypes over a type whose container converts, and types given `try_from_sexp_via_str_parse!`"
160)]
161pub trait TryFromSexpElement: Sized {
162    /// The type whose containers are read first.
163    type Inner;
164
165    /// Wrap an inner value into `Self`.
166    fn from_inner(inner: Self::Inner) -> Self;
167
168    /// Refuse an R value before it is read: `Err` for a value the inner type
169    /// would accept but `Self` must not, such as a number that carries a unit
170    /// class (`difftime`, `Date`, `POSIXct`).
171    ///
172    /// Called with the input of every shape, before its inner conversion: by
173    /// the scalar `TryFromSexp` (the derive's calls it), for `Option<Self>`
174    /// on any input but `NULL`, which stays "not given", and for `Vec<Self>`
175    /// and `Vec<Option<Self>>` where R keeps the element's class: once on the
176    /// whole vector for an atomic inner type, on each element (but `NULL`)
177    /// for a list of R objects (`Self::Inner` is `List`, or a newtype over
178    /// it). It sees the value as R holds it, attributes and all, before
179    /// anything is read from it.
180    ///
181    /// Return an [`RError`](crate::condition::RError), or any
182    /// [`RConditionData`](crate::condition::RConditionData) type, with `?`
183    /// or `.into()` to give the refusal its own classes and fields
184    /// ([`SexpError::Condition`]). As an
185    /// argument error it keeps the wrapper's context: `'<p>' must be
186    /// <expected>: <message>`, the classes before the crate's
187    /// `conversion_error_class`, `e$param` and `e$rust_type`. A plain
188    /// `SexpError` (`SexpError::InvalidValue`) works too, with no classes.
189    ///
190    /// The default accepts every value; it is what the derive emits without
191    /// `validate` and what a string-parsed type has.
192    #[inline]
193    fn check_sexp(_sexp: SEXP) -> Result<(), SexpError> {
194        Ok(())
195    }
196}
197
198/// Unwrap a forwarding newtype into its inner value (Rust → R side).
199///
200/// Emitted by `#[derive(IntoR)]`. Powers the `IntoR` container blankets for
201/// `Option<T>` / `Vec<Option<T>>` in this module.
202pub trait IntoRNewtype {
203    /// The wrapped inner type, whose conversions are forwarded to.
204    type Inner;
205
206    /// Unwrap the newtype into its inner value.
207    fn into_inner(self) -> Self::Inner;
208}
209
210/// How a `Vec<Self>` becomes a single R vector SEXP.
211///
212/// This is the shared element-marker behind the **one** `impl<T: …> IntoR for
213/// Vec<T>` blanket slot. Implemented concretely per type — by `#[derive(IntoR)]`
214/// for newtypes (forwarding to `Vec<Inner>`), and by the `MatchArg` bridge in
215/// `match_arg.rs` for `match.arg` enums (STRSXP by variant name). See the module
216/// docs for why this cannot be two competing blankets.
217pub trait IntoRVecElement: Sized {
218    /// Convert all elements into one R vector SEXP.
219    fn elements_into_sexp(values: Vec<Self>) -> SEXP;
220}
221
222// endregion
223
224// region: container helpers (bound on the inner type itself, #1682)
225
226/// `Vec<Self>: TryFromSexp` with an error that converts into [`SexpError`],
227/// with `Self` as the bound's self type.
228///
229/// Plumbing for the `Vec<T>` blanket below: see the module docs (#1682).
230#[doc(hidden)]
231pub trait VecInner: Sized {
232    /// `<Vec<Self> as TryFromSexp>::NATIVE_BORROW`.
233    const VEC_NATIVE_BORROW: Option<NativeBorrow>;
234    /// `<Vec<Self> as TryFromSexp>::CHARACTER_ONLY`.
235    const VEC_CHARACTER_ONLY: bool;
236    /// `<Vec<Self> as TryFromSexp>::__mx_try_from_sexp_with_check`: the
237    /// vector, with `check` run where `Vec<Self>` keeps an element's class.
238    fn vec_try_from_sexp(sexp: SEXP, check: &ElementCheck<'_>) -> Result<Vec<Self>, SexpError>;
239    /// `<Vec<Self> as TryFromSexp>::__mx_try_from_sexp_unchecked_with_check`.
240    ///
241    /// # Safety
242    ///
243    /// As [`TryFromSexp::try_from_sexp_unchecked`].
244    unsafe fn vec_try_from_sexp_unchecked(
245        sexp: SEXP,
246        check: &ElementCheck<'_>,
247    ) -> Result<Vec<Self>, SexpError>;
248}
249
250impl<I> VecInner for I
251where
252    Vec<I>: TryFromSexp,
253    <Vec<I> as TryFromSexp>::Error: Into<SexpError>,
254{
255    const VEC_NATIVE_BORROW: Option<NativeBorrow> = <Vec<I> as TryFromSexp>::NATIVE_BORROW;
256    const VEC_CHARACTER_ONLY: bool = <Vec<I> as TryFromSexp>::CHARACTER_ONLY;
257
258    #[inline]
259    fn vec_try_from_sexp(sexp: SEXP, check: &ElementCheck<'_>) -> Result<Vec<Self>, SexpError> {
260        <Vec<I> as TryFromSexp>::__mx_try_from_sexp_with_check(sexp, check)
261    }
262
263    #[inline]
264    unsafe fn vec_try_from_sexp_unchecked(
265        sexp: SEXP,
266        check: &ElementCheck<'_>,
267    ) -> Result<Vec<Self>, SexpError> {
268        unsafe { <Vec<I> as TryFromSexp>::__mx_try_from_sexp_unchecked_with_check(sexp, check) }
269    }
270}
271
272/// `Option<Self>: TryFromSexp` with an error that converts into
273/// [`SexpError`], with `Self` as the bound's self type.
274///
275/// Plumbing for the `Option<T>` blanket below: see the module docs (#1682).
276#[doc(hidden)]
277pub trait OptionInner: Sized {
278    /// `<Option<Self> as TryFromSexp>::NATIVE_BORROW`.
279    const OPTION_NATIVE_BORROW: Option<NativeBorrow>;
280    /// `<Option<Self> as TryFromSexp>::CHARACTER_ONLY`.
281    const OPTION_CHARACTER_ONLY: bool;
282    /// `<Option<Self> as TryFromSexp>::try_from_sexp`, its error as a
283    /// [`SexpError`].
284    fn option_try_from_sexp(sexp: SEXP) -> Result<Option<Self>, SexpError>;
285    /// `<Option<Self> as TryFromSexp>::try_from_sexp_unchecked`, its error as
286    /// a [`SexpError`].
287    ///
288    /// # Safety
289    ///
290    /// As [`TryFromSexp::try_from_sexp_unchecked`].
291    unsafe fn option_try_from_sexp_unchecked(sexp: SEXP) -> Result<Option<Self>, SexpError>;
292}
293
294impl<I> OptionInner for I
295where
296    Option<I>: TryFromSexp,
297    <Option<I> as TryFromSexp>::Error: Into<SexpError>,
298{
299    const OPTION_NATIVE_BORROW: Option<NativeBorrow> = <Option<I> as TryFromSexp>::NATIVE_BORROW;
300    const OPTION_CHARACTER_ONLY: bool = <Option<I> as TryFromSexp>::CHARACTER_ONLY;
301
302    #[inline]
303    fn option_try_from_sexp(sexp: SEXP) -> Result<Option<Self>, SexpError> {
304        <Option<I> as TryFromSexp>::try_from_sexp(sexp).map_err(Into::into)
305    }
306
307    #[inline]
308    unsafe fn option_try_from_sexp_unchecked(sexp: SEXP) -> Result<Option<Self>, SexpError> {
309        unsafe { <Option<I> as TryFromSexp>::try_from_sexp_unchecked(sexp) }.map_err(Into::into)
310    }
311}
312
313/// `Vec<Option<Self>>: TryFromSexp` with an error that converts into
314/// [`SexpError`], with `Self` as the bound's self type.
315///
316/// Plumbing for the `Vec<Option<T>>` blanket below: see the module docs
317/// (#1682).
318#[doc(hidden)]
319pub trait VecOptionInner: Sized {
320    /// `<Vec<Option<Self>> as TryFromSexp>::NATIVE_BORROW`.
321    const VEC_OPTION_NATIVE_BORROW: Option<NativeBorrow>;
322    /// `<Vec<Option<Self>> as TryFromSexp>::CHARACTER_ONLY`.
323    const VEC_OPTION_CHARACTER_ONLY: bool;
324    /// `<Vec<Option<Self>> as TryFromSexp>::__mx_try_from_sexp_with_check`:
325    /// the vector, with `check` run where `Vec<Option<Self>>` keeps an
326    /// element's class.
327    fn vec_option_try_from_sexp(
328        sexp: SEXP,
329        check: &ElementCheck<'_>,
330    ) -> Result<Vec<Option<Self>>, SexpError>;
331    /// `<Vec<Option<Self>> as TryFromSexp>::__mx_try_from_sexp_unchecked_with_check`.
332    ///
333    /// # Safety
334    ///
335    /// As [`TryFromSexp::try_from_sexp_unchecked`].
336    unsafe fn vec_option_try_from_sexp_unchecked(
337        sexp: SEXP,
338        check: &ElementCheck<'_>,
339    ) -> Result<Vec<Option<Self>>, SexpError>;
340}
341
342impl<I> VecOptionInner for I
343where
344    Vec<Option<I>>: TryFromSexp,
345    <Vec<Option<I>> as TryFromSexp>::Error: Into<SexpError>,
346{
347    const VEC_OPTION_NATIVE_BORROW: Option<NativeBorrow> =
348        <Vec<Option<I>> as TryFromSexp>::NATIVE_BORROW;
349    const VEC_OPTION_CHARACTER_ONLY: bool = <Vec<Option<I>> as TryFromSexp>::CHARACTER_ONLY;
350
351    #[inline]
352    fn vec_option_try_from_sexp(
353        sexp: SEXP,
354        check: &ElementCheck<'_>,
355    ) -> Result<Vec<Option<Self>>, SexpError> {
356        <Vec<Option<I>> as TryFromSexp>::__mx_try_from_sexp_with_check(sexp, check)
357    }
358
359    #[inline]
360    unsafe fn vec_option_try_from_sexp_unchecked(
361        sexp: SEXP,
362        check: &ElementCheck<'_>,
363    ) -> Result<Vec<Option<Self>>, SexpError> {
364        unsafe {
365            <Vec<Option<I>> as TryFromSexp>::__mx_try_from_sexp_unchecked_with_check(sexp, check)
366        }
367    }
368}
369
370// endregion
371
372// region: IntoR for Vec<T> — the unified element-marker blanket
373
374/// The single `IntoR for Vec<T>` blanket, shared by `MatchArg` enums and
375/// `#[derive(IntoR)]` newtypes via [`IntoRVecElement`]. Coexists with the
376/// concrete `impl IntoR for Vec<i32>` (etc.) impls: `IntoRVecElement` is
377/// crate-local, so coherence proves the foreign R-native types do not implement
378/// it.
379impl<T: IntoRVecElement> IntoR for Vec<T> {
380    type Error = std::convert::Infallible;
381
382    #[inline]
383    fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
384        Ok(self.into_sexp())
385    }
386
387    #[inline]
388    unsafe fn try_into_sexp_unchecked(self) -> Result<SEXP, Self::Error> {
389        self.try_into_sexp()
390    }
391
392    #[inline]
393    fn into_sexp(self) -> SEXP {
394        T::elements_into_sexp(self)
395    }
396}
397
398// endregion
399
400// region: TryFromSexp container blankets (R → Rust)
401
402// The `Vec` blankets hand `T::check_sexp`, after the check of any enclosing
403// newtype, to the inner container's `__mx_try_from_sexp_with_check`, which
404// runs it where that container keeps an element's class: once on the whole
405// input for an atomic vector, on each element for a list of R objects
406// (#1837). They then wrap what was read with `T::from_inner`. `Option<T>`
407// runs the check itself, on a non-`NULL` input. None calls
408// `T::try_from_sexp`: see "Forwarding" on `TryFromSexpElement`.
409
410impl<T: TryFromSexpElement> TryFromSexp for Vec<T>
411where
412    T::Inner: VecInner,
413{
414    type Error = SexpError;
415    const NATIVE_BORROW: Option<NativeBorrow> = <T::Inner as VecInner>::VEC_NATIVE_BORROW;
416    const CHARACTER_ONLY: bool = <T::Inner as VecInner>::VEC_CHARACTER_ONLY;
417
418    #[inline]
419    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
420        Self::__mx_try_from_sexp_with_check(sexp, &no_element_check)
421    }
422
423    #[inline]
424    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
425        unsafe { Self::__mx_try_from_sexp_unchecked_with_check(sexp, &no_element_check) }
426    }
427
428    #[inline]
429    fn __mx_try_from_sexp_with_check(
430        sexp: SEXP,
431        check: &ElementCheck<'_>,
432    ) -> Result<Self, SexpError> {
433        let check = |x: SEXP| {
434            check(x)?;
435            T::check_sexp(x)
436        };
437        Ok(<T::Inner as VecInner>::vec_try_from_sexp(sexp, &check)?
438            .into_iter()
439            .map(T::from_inner)
440            .collect())
441    }
442
443    #[inline]
444    unsafe fn __mx_try_from_sexp_unchecked_with_check(
445        sexp: SEXP,
446        check: &ElementCheck<'_>,
447    ) -> Result<Self, SexpError> {
448        let check = |x: SEXP| {
449            check(x)?;
450            T::check_sexp(x)
451        };
452        Ok(
453            unsafe { <T::Inner as VecInner>::vec_try_from_sexp_unchecked(sexp, &check) }?
454                .into_iter()
455                .map(T::from_inner)
456                .collect(),
457        )
458    }
459}
460
461// `T: TryFromSexp` lets `__mx_has_na` ask the element itself
462// (`TryFromSexpElement` has no inner accessor, and an override cannot add a
463// bound, E0276). The derive and the str-parse macro always emit both impls.
464impl<T: TryFromSexpElement + TryFromSexp> TryFromSexp for Option<T>
465where
466    T::Inner: OptionInner,
467{
468    type Error = SexpError;
469    const NATIVE_BORROW: Option<NativeBorrow> = <T::Inner as OptionInner>::OPTION_NATIVE_BORROW;
470    const CHARACTER_ONLY: bool = <T::Inner as OptionInner>::OPTION_CHARACTER_ONLY;
471    // `NULL` (`None`) is "not given" and passes `no_na`.
472    #[inline]
473    fn __mx_has_na(&self) -> bool {
474        self.as_ref().is_some_and(T::__mx_has_na)
475    }
476
477    // `NULL` is "not given" too, so the check sees only a value.
478    #[inline]
479    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
480        if sexp.type_of() != SEXPTYPE::NILSXP {
481            T::check_sexp(sexp)?;
482        }
483        Ok(<T::Inner as OptionInner>::option_try_from_sexp(sexp)?.map(T::from_inner))
484    }
485
486    #[inline]
487    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
488        if sexp.type_of() != SEXPTYPE::NILSXP {
489            T::check_sexp(sexp)?;
490        }
491        Ok(
492            unsafe { <T::Inner as OptionInner>::option_try_from_sexp_unchecked(sexp) }?
493                .map(T::from_inner),
494        )
495    }
496}
497
498impl<T: TryFromSexpElement> TryFromSexp for Vec<Option<T>>
499where
500    T::Inner: VecOptionInner,
501{
502    type Error = SexpError;
503    const NATIVE_BORROW: Option<NativeBorrow> =
504        <T::Inner as VecOptionInner>::VEC_OPTION_NATIVE_BORROW;
505    const CHARACTER_ONLY: bool = <T::Inner as VecOptionInner>::VEC_OPTION_CHARACTER_ONLY;
506
507    #[inline]
508    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
509        Self::__mx_try_from_sexp_with_check(sexp, &no_element_check)
510    }
511
512    #[inline]
513    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
514        unsafe { Self::__mx_try_from_sexp_unchecked_with_check(sexp, &no_element_check) }
515    }
516
517    #[inline]
518    fn __mx_try_from_sexp_with_check(
519        sexp: SEXP,
520        check: &ElementCheck<'_>,
521    ) -> Result<Self, SexpError> {
522        let check = |x: SEXP| {
523            check(x)?;
524            T::check_sexp(x)
525        };
526        Ok(
527            <T::Inner as VecOptionInner>::vec_option_try_from_sexp(sexp, &check)?
528                .into_iter()
529                .map(|opt| opt.map(T::from_inner))
530                .collect(),
531        )
532    }
533
534    #[inline]
535    unsafe fn __mx_try_from_sexp_unchecked_with_check(
536        sexp: SEXP,
537        check: &ElementCheck<'_>,
538    ) -> Result<Self, SexpError> {
539        let check = |x: SEXP| {
540            check(x)?;
541            T::check_sexp(x)
542        };
543        Ok(unsafe {
544            <T::Inner as VecOptionInner>::vec_option_try_from_sexp_unchecked(sexp, &check)
545        }?
546        .into_iter()
547        .map(|opt| opt.map(T::from_inner))
548        .collect())
549    }
550}
551
552// endregion
553
554// region: IntoR container blankets (Rust → R) for Vec<Option>
555
556// NOTE: there is deliberately no `impl<T: IntoRNewtype> IntoR for Option<T>`.
557// That bare `Option<T>` blanket collides (E0119) with the pre-existing
558// `impl<T: Copy + IntoR> IntoR for Option<&T>` (into_r/large_integers.rs): `&T`
559// is `#[fundamental]`, so a downstream crate *could* implement `IntoRNewtype`
560// for `&LocalType`, and coherence cannot prove the two disjoint. Returning a
561// `Option<MyNewtype>` to R is the one shape the derive does not grant — map to
562// the inner first (`opt.map(|x| x.0)` → `Option<Inner>`), which mirrors the
563// NULL-vs-NA guidance already on the `Option<&T>` impl. See issue #844.
564
565impl<T: IntoRNewtype> IntoR for Vec<Option<T>>
566where
567    Vec<Option<T::Inner>>: IntoR,
568{
569    type Error = <Vec<Option<T::Inner>> as IntoR>::Error;
570
571    #[inline]
572    fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
573        self.into_iter()
574            .map(|opt| opt.map(T::into_inner))
575            .collect::<Vec<Option<T::Inner>>>()
576            .try_into_sexp()
577    }
578
579    #[inline]
580    unsafe fn try_into_sexp_unchecked(self) -> Result<SEXP, Self::Error> {
581        unsafe {
582            self.into_iter()
583                .map(|opt| opt.map(T::into_inner))
584                .collect::<Vec<Option<T::Inner>>>()
585                .try_into_sexp_unchecked()
586        }
587    }
588
589    #[inline]
590    fn into_sexp(self) -> SEXP {
591        self.into_iter()
592            .map(|opt| opt.map(T::into_inner))
593            .collect::<Vec<Option<T::Inner>>>()
594            .into_sexp()
595    }
596
597    #[inline]
598    unsafe fn into_sexp_unchecked(self) -> SEXP {
599        unsafe {
600            self.into_iter()
601                .map(|opt| opt.map(T::into_inner))
602                .collect::<Vec<Option<T::Inner>>>()
603                .into_sexp_unchecked()
604        }
605    }
606}
607
608// endregion