Skip to main content

miniextendr_api/from_r/
rooted.rs

1//! Conversions whose borrowed result cannot outlive an R root.
2
3use super::{
4    NativeBorrow, SexpError, SexpTypeError, TryFromSexp, charsxp_to_cow, charsxp_to_str,
5    map_strsxp_with, map_vecsxp_batched,
6};
7use crate::{RCow, RNativeType, Root, SEXP, SEXPTYPE, SexpExt, StrVec};
8use std::borrow::Cow;
9
10/// Convert an R value while retaining its root's lifetime in the result.
11///
12/// This conversion carries the root lifetime that raw [`TryFromSexp`]
13/// cannot express.
14/// Generated wrappers use the rooted `.Call` argument for `StrVec`, `RCow`,
15/// and `Cow` inputs (including the character-vector Cow containers). Main-thread
16/// wrappers and trait shims also use it for explicit shared native references
17/// and optional shared references. Worker shared references copy to Rust-owned
18/// scalars, vectors or strings before dispatch, then borrow inside the worker.
19/// Nested shared-reference `Vec` inputs retain every list element in the root's
20/// protection scope, even when an ALTREP accessor does not cache that element.
21/// A type alias can use this trait explicitly in the function body.
22///
23/// A root proves liveness, not exclusive access: this trait does not provide
24/// mutable references to R storage. Shared native borrows still participate
25/// in a wrapper's alias checks against its other, mutable arguments.
26///
27/// ```compile_fail
28/// use miniextendr_api::{Root, TryFromRoot};
29/// use std::borrow::Cow;
30/// fn escape<'a>(root: Root<'a>) -> Cow<'static, [i32]> {
31///     Cow::try_from_root(root).unwrap()
32/// }
33/// ```
34/// ```compile_fail
35/// use miniextendr_api::{Root, TryFromRoot};
36/// use std::borrow::Cow;
37/// fn escape<'a>(root: Root<'a>) -> Vec<Option<Cow<'static, str>>> {
38///     Vec::try_from_root(root).unwrap()
39/// }
40/// ```
41/// ```compile_fail
42/// use miniextendr_api::{Root, TryFromRoot};
43/// fn escape<'a>(root: Root<'a>) -> &'static [i32] {
44///     <&[i32]>::try_from_root(root).unwrap()
45/// }
46/// ```
47/// ```compile_fail
48/// use miniextendr_api::{Root, TryFromRoot};
49/// fn escape<'a>(root: Root<'a>) -> Option<&'static str> {
50///     <Option<&str>>::try_from_root(root).unwrap()
51/// }
52/// ```
53/// ```compile_fail
54/// use miniextendr_api::{Root, TryFromRoot};
55/// fn escape<'a>(root: Root<'a>) -> Vec<Vec<&'static [i32]>> {
56///     Vec::try_from_root(root).unwrap()
57/// }
58/// ```
59pub trait TryFromRoot<'a>: Sized {
60    /// The error reported when the R representation is not accepted.
61    type Error;
62
63    /// Native storage borrowed by this conversion, for wrapper alias checks.
64    #[doc(hidden)]
65    const NATIVE_BORROW: Option<NativeBorrow> = None;
66
67    /// Convert without allowing an R-backed borrow to outlive `root`.
68    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error>;
69}
70
71/// A shared reference converts as its raw `'static` form does, with the same
72/// checks, errors and alias metadata, and so does a custom `TryFromSexp` impl
73/// for a reference to the user's own type. Only the lifetime changes: the
74/// result borrows from the root, not from `'static`.
75impl<'a, T: ?Sized + 'static> TryFromRoot<'a> for &'a T
76where
77    &'static T: TryFromSexp,
78{
79    type Error = <&'static T as TryFromSexp>::Error;
80    const NATIVE_BORROW: Option<NativeBorrow> = <&'static T as TryFromSexp>::NATIVE_BORROW;
81
82    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error> {
83        // Narrow the raw reference to 'a. String converters also retain the
84        // CHARSXP, which an ALTREP parent need not keep after Elt returns.
85        unsafe { <&'static T as TryFromSexp>::__mx_try_from_rooted(root) }
86    }
87}
88
89/// `NULL` (and `NA` for strings) is `None`, as in the raw conversion.
90impl<'a, T: ?Sized + 'static> TryFromRoot<'a> for Option<&'a T>
91where
92    Option<&'static T>: TryFromSexp,
93{
94    type Error = <Option<&'static T> as TryFromSexp>::Error;
95    const NATIVE_BORROW: Option<NativeBorrow> = <Option<&'static T> as TryFromSexp>::NATIVE_BORROW;
96
97    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error> {
98        // The result is narrowed to the lifetime of this root.
99        unsafe { <Option<&'static T> as TryFromSexp>::__mx_try_from_rooted(root) }
100    }
101}
102
103impl<'a> TryFromRoot<'a> for StrVec<'a> {
104    type Error = SexpError;
105
106    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error> {
107        StrVec::try_from_root(root)
108    }
109}
110
111impl<'a, T: RNativeType> TryFromRoot<'a> for RCow<'a, T> {
112    type Error = SexpTypeError;
113    const NATIVE_BORROW: Option<NativeBorrow> = RCow::<'a, T>::NATIVE_BORROW;
114
115    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error> {
116        RCow::try_from_root(root)
117    }
118}
119
120impl<'a, T: RNativeType> TryFromRoot<'a> for Cow<'a, [T]> {
121    type Error = SexpTypeError;
122    const NATIVE_BORROW: Option<NativeBorrow> = <&[T] as TryFromRoot<'a>>::NATIVE_BORROW;
123
124    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error> {
125        <&[T]>::try_from_root(root).map(Cow::Borrowed)
126    }
127}
128
129impl<'a> TryFromRoot<'a> for Cow<'a, str> {
130    type Error = SexpError;
131
132    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error> {
133        <&str>::try_from_root(root).map(Cow::Borrowed)
134    }
135}
136
137/// An element type of a `Vec` that converts through [`TryFromRoot`]. The
138/// element decides what the vector reads: character elements read a `STRSXP`,
139/// the others read a list. One `Vec<E>` impl over this trait, rather than one
140/// impl per element shape, keeps a nested `Vec<Vec<..>>` from sending type
141/// inference into unbounded recursion when the element type is not yet known.
142#[doc(hidden)]
143pub trait RootedVecElement<'a>: Sized {
144    /// Native storage the vector borrows, for wrapper alias checks.
145    const NATIVE_BORROW: Option<NativeBorrow>;
146
147    /// Convert every element, retaining each one an ALTREP accessor returns.
148    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError>;
149}
150
151impl<'a, E: RootedVecElement<'a>> TryFromRoot<'a> for Vec<E> {
152    type Error = SexpError;
153    const NATIVE_BORROW: Option<NativeBorrow> = E::NATIVE_BORROW;
154
155    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error> {
156        E::vec_from_root(root)
157    }
158}
159
160/// `NULL` is `None`; anything else converts as the `Vec`.
161impl<'a, E: RootedVecElement<'a>> TryFromRoot<'a> for Option<Vec<E>> {
162    type Error = SexpError;
163    const NATIVE_BORROW: Option<NativeBorrow> = E::NATIVE_BORROW;
164
165    fn try_from_root(root: Root<'a>) -> Result<Self, Self::Error> {
166        if root.get().type_of() == SEXPTYPE::NILSXP {
167            Ok(None)
168        } else {
169            E::vec_from_root(root).map(Some)
170        }
171    }
172}
173
174/// Character elements keep the raw conversions' NA handling: `NA` reads as
175/// `""` for the plain forms and as `None` for the optional ones.
176macro_rules! character_elements {
177    ($life:lifetime; $($element:ty => |$value:ident| $borrow:expr, $na:expr, $blank:expr;)*) => {$(
178        impl<$life> RootedVecElement<$life> for $element {
179            const NATIVE_BORROW: Option<NativeBorrow> = None;
180
181            fn vec_from_root(root: Root<$life>) -> Result<Vec<Self>, SexpError> {
182                map_strsxp_with(root.get(), |value, _| {
183                    if value == SEXP::na_string() {
184                        Ok($na)
185                    } else if value == SEXP::blank_string() {
186                        Ok($blank)
187                    } else {
188                        // An ALTSTRING accessor need not retain this CHARSXP.
189                        let $value = unsafe { root.protect_child(value) }.get();
190                        Ok($borrow)
191                    }
192                })
193            }
194        }
195    )*};
196}
197
198impl<'a> RootedVecElement<'a> for &'a str {
    const NATIVE_BORROW: Option<NativeBorrow> = None;
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        map_strsxp_with(root.get(),
            |value, _|
                {
                    if value == SEXP::na_string() {
                        Ok("")
                    } else if value == SEXP::blank_string() {
                        Ok("")
                    } else {
                        let value = unsafe { root.protect_child(value) }.get();
                        Ok(unsafe { charsxp_to_str(value) })
                    }
                })
    }
}
impl<'a> RootedVecElement<'a> for Option<&'a str> {
    const NATIVE_BORROW: Option<NativeBorrow> = None;
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        map_strsxp_with(root.get(),
            |value, _|
                {
                    if value == SEXP::na_string() {
                        Ok(None)
                    } else if value == SEXP::blank_string() {
                        Ok(Some(""))
                    } else {
                        let value = unsafe { root.protect_child(value) }.get();
                        Ok(Some(unsafe { charsxp_to_str(value) }))
                    }
                })
    }
}
impl<'a> RootedVecElement<'a> for Cow<'a, str> {
    const NATIVE_BORROW: Option<NativeBorrow> = None;
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        map_strsxp_with(root.get(),
            |value, _|
                {
                    if value == SEXP::na_string() {
                        Ok(Cow::Borrowed(""))
                    } else if value == SEXP::blank_string() {
                        Ok(Cow::Borrowed(""))
                    } else {
                        let value = unsafe { root.protect_child(value) }.get();
                        Ok(unsafe { charsxp_to_cow(value) })
                    }
                })
    }
}
impl<'a> RootedVecElement<'a> for Option<Cow<'a, str>> {
    const NATIVE_BORROW: Option<NativeBorrow> = None;
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        map_strsxp_with(root.get(),
            |value, _|
                {
                    if value == SEXP::na_string() {
                        Ok(None)
                    } else if value == SEXP::blank_string() {
                        Ok(Some(Cow::Borrowed("")))
                    } else {
                        let value = unsafe { root.protect_child(value) }.get();
                        Ok(Some(unsafe { charsxp_to_cow(value) }))
                    }
                })
    }
}character_elements! {'a;
199    &'a str => |value| unsafe { charsxp_to_str(value) }, "", "";
200    Option<&'a str> => |value| Some(unsafe { charsxp_to_str(value) }), None, Some("");
201    Cow<'a, str> => |value| unsafe { charsxp_to_cow(value) }, Cow::Borrowed(""), Cow::Borrowed("");
202    Option<Cow<'a, str>> => |value| Some(unsafe { charsxp_to_cow(value) }), None, Some(Cow::Borrowed(""));
203}
204
205/// Each element gets its own root before conversion or the next Elt callback.
206/// Merely rooting an ALTREP list does not retain transient Elt results.
207fn rooted_list<'a, T: TryFromRoot<'a>>(root: Root<'a>) -> Result<Vec<T>, SexpError>
208where
209    T::Error: Into<SexpError>,
210{
211    map_vecsxp_batched(root.get(), |value| {
212        T::try_from_root(unsafe { root.protect_child(value) }).map_err(Into::into)
213    })
214}
215
216/// List elements: native references (for the native types that have a raw
217/// reference conversion), and vectors of any element, nested to any depth.
218macro_rules! list_elements {
219    ($life:lifetime, $param:ident: $bound:path; $($element:ty),* $(,)?) => {$(
220        impl<$life, $param: $bound> RootedVecElement<$life> for $element
221        where
222            $element: TryFromRoot<$life>,
223            <$element as TryFromRoot<$life>>::Error: Into<SexpError>,
224        {
225            const NATIVE_BORROW: Option<NativeBorrow> =
226                NativeBorrow::in_list(<$element as TryFromRoot<$life>>::NATIVE_BORROW);
227
228            fn vec_from_root(root: Root<$life>) -> Result<Vec<Self>, SexpError> {
229                rooted_list(root)
230            }
231        }
232    )*};
233}
234
235impl<'a, T: RNativeType> RootedVecElement<'a> for &'a T where
    &'a T: TryFromRoot<'a>, <&'a T as TryFromRoot<'a>>::Error: Into<SexpError>
    {
    const NATIVE_BORROW: Option<NativeBorrow> =
        NativeBorrow::in_list(<&'a T as TryFromRoot<'a>>::NATIVE_BORROW);
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        rooted_list(root)
    }
}
impl<'a, T: RNativeType> RootedVecElement<'a> for Option<&'a T> where
    Option<&'a T>: TryFromRoot<'a>,
    <Option<&'a T> as TryFromRoot<'a>>::Error: Into<SexpError> {
    const NATIVE_BORROW: Option<NativeBorrow> =
        NativeBorrow::in_list(<Option<&'a T> as
                TryFromRoot<'a>>::NATIVE_BORROW);
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        rooted_list(root)
    }
}
impl<'a, T: RNativeType> RootedVecElement<'a> for &'a [T] where
    &'a [T]: TryFromRoot<'a>,
    <&'a [T] as TryFromRoot<'a>>::Error: Into<SexpError> {
    const NATIVE_BORROW: Option<NativeBorrow> =
        NativeBorrow::in_list(<&'a [T] as TryFromRoot<'a>>::NATIVE_BORROW);
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        rooted_list(root)
    }
}
impl<'a, T: RNativeType> RootedVecElement<'a> for Option<&'a [T]> where
    Option<&'a [T]>: TryFromRoot<'a>,
    <Option<&'a [T]> as TryFromRoot<'a>>::Error: Into<SexpError> {
    const NATIVE_BORROW: Option<NativeBorrow> =
        NativeBorrow::in_list(<Option<&'a [T]> as
                TryFromRoot<'a>>::NATIVE_BORROW);
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        rooted_list(root)
    }
}list_elements!('a, T: RNativeType; &'a T, Option<&'a T>, &'a [T], Option<&'a [T]>);
236impl<'a, E: RootedVecElement<'a>> RootedVecElement<'a> for Vec<E> where
    Vec<E>: TryFromRoot<'a>,
    <Vec<E> as TryFromRoot<'a>>::Error: Into<SexpError> {
    const NATIVE_BORROW: Option<NativeBorrow> =
        NativeBorrow::in_list(<Vec<E> as TryFromRoot<'a>>::NATIVE_BORROW);
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        rooted_list(root)
    }
}
impl<'a, E: RootedVecElement<'a>> RootedVecElement<'a> for Option<Vec<E>>
    where Option<Vec<E>>: TryFromRoot<'a>,
    <Option<Vec<E>> as TryFromRoot<'a>>::Error: Into<SexpError> {
    const NATIVE_BORROW: Option<NativeBorrow> =
        NativeBorrow::in_list(<Option<Vec<E>> as
                TryFromRoot<'a>>::NATIVE_BORROW);
    fn vec_from_root(root: Root<'a>) -> Result<Vec<Self>, SexpError> {
        rooted_list(root)
    }
}list_elements!('a, E: RootedVecElement<'a>; Vec<E>, Option<Vec<E>>);