31 A print helper
Write a function that reads a list of external pointers and turns it back into a character vector.
When we print our list of objects, it’s ugly:
parse_shapes(c("triangle", "hexagon"))
#> [[1]]
#> <pointer: 0x600000abc120>
#>
#> [[2]]
#> <pointer: 0x600000abc480>R can’t see inside an external pointer, so all it can show is the memory address. We’d much rather see something meaningful. To do that, we write a function that walks the list, gets each Rust value back out, and produces a character vector.
Accessing the external pointer
- External pointers are stored in a struct called….
ExternalPtr, this is a fancy type of struct - It has an associated type—as seen by this “turbo-fish”
- Rust needs to know what type is inside of the pointer so that it can give you access to the methods in it
- Each type in Rust has a special method called
try_from()that fallibly tries to convert. If it can’t, we get an error! - extendr provides this
ExternalPtr::try_from()so that we can try and get the pointer!
Here’s how this works—since we refer to the type using Self this will work for any type with #[extendr] on it.
impl RShape {
fn get_extptr(robj: Robj) -> extendr_api::Result<ExternalPtr<Self>> {
ExternalPtr::<Self>::try_from(robj)
}
}Iterating a list
- When we iterate over a
List, each item is a tuple - It is a fixed sized thing where each element is unnamed and can have a different type
- The first element (accessed via
.0) is the name as a&str, and the second element (via.1) is the value as anRobj
We usually ignore the name with _.
For each value we try to get the pointer back. If we succeed, we borrow the inner Rust value and produce a string from it. If not, we produce an NA.
Example
Here we turn a list of RShapes into a character vector of their vertex counts:
#[extendr]
fn format_shapes(x: List) -> Strings {
x.into_iter()
.map(|(_, robj)| match RShape::get_extptr(robj) {
Ok(v) => Rstr::from(format!("{:?}", v.0)),
Err(_) => Rstr::na(),
})
.collect()
}Exercise
Note that instead of using format!() you’ll use .to_string().
- Write
format_rurls(x: List) -> Strings - Iterate the list, destructuring each element as
(_, vi) - Use
RUrl::get_extptr()to get the pointer back - On success, turn the inner
Urlinto a string with.to_string() - On failure, return
Rstr::na()
View solution
#[extendr]
fn format_rurls(x: List) -> Strings {
x.into_iter()
.map(|(_, vi)| {
let vv = RUrl::get_extptr(vi);
match vv {
Ok(rurl) => Rstr::from(rurl.0.to_string()),
Err(_) => Rstr::na(),
}
})
.collect::<Strings>()
}