Expand description
Container conversions for forwarding newtypes (#[derive(TryFromSexp)] /
#[derive(IntoR)]). See the module docs and issue #844.
Container conversions for forwarding newtypes.
#[derive(TryFromSexp)] / #[derive(IntoR)] on a single-field newtype
(struct UserId(Uuid)) emit the scalar forwarding impls and a small marker
impl from this module. The container blankets here then light up
Vec<UserId>, Option<UserId>, and Vec<Option<UserId>> automatically — the
newtype inherits the inner type’s exact SEXPTYPE checks, NA policy, and error
text in every shape.
§Why the markers live here and not in the derive
A downstream crate cannot write impl TryFromSexp for Vec<MyNewtype>: Vec /
Option are not #[fundamental], so the local newtype is “covered” and the
orphan rule (E0117) forbids it. The container impls must live in
miniextendr-api, keyed on a marker trait the derive can legally implement
downstream (foreign trait + local Self). The blankets below are those
container impls. See analysis/rconvert-containers-coherence-2026-06-04.md
(issue #844) for the full coherence analysis.
FromRNewtype / IntoRNewtype / IntoRVecElement are plumbing:
they are emitted by the derives, not implemented by hand. Implementing them
manually is supported but unusual.
§The asymmetries
Five of the six container shapes are granted. Two are not, for two different coherence reasons:
IntoR for Vec<T>(granted, but shared). This slot has exactly one blanket andMatchArgalready needs it (Vec<MyEnum>→ STRSXP). Rather than a second, conflictingVec<T>blanket (E0119), both paths funnel throughIntoRVecElement:MatchArgtypes reach it via a bridge blanket inmatch_arg.rs, newtypes via a concrete impl emitted by#[derive(IntoR)]. A type that is both aMatchArgenum and anIntoRnewtype is a coherence error — don’t derive both on one type.IntoR for Option<T>(not granted). A bareOption<T>blanket collides with the pre-existingimpl<T: Copy + IntoR> IntoR for Option<&T>:&Tis#[fundamental], so a downstream crate could implIntoRNewtypefor&LocalTypeand coherence cannot prove the two disjoint. ReturnOption<Inner>(opt.map(|x| x.0)) instead. See the note on the missing blanket below.
TryFromSexp for Vec<T> / Option<T> / Vec<Option<T>> and IntoR for Vec<Option<T>> are coherence-free: no other blanket occupies those slots.
Traits§
- FromR
Newtype - Construct a forwarding newtype from its inner value (R → Rust side).
- IntoR
Newtype - Unwrap a forwarding newtype into its inner value (Rust → R side).
- IntoR
VecElement - How a
Vec<Self>becomes a single R vector SEXP.