28 R’s Type System
Get a basic grip on R’s type system and develop an intuition for how Rust types map to R types.
In R, everything is an SEXP.

All SEXP types
| no | SEXPTYPE |
Description |
|---|---|---|
| 0 | NILSXP |
NULL |
| 1 | SYMSXP |
symbols |
| 2 | LISTSXP |
pairlists |
| 3 | CLOSXP |
closures |
| 4 | ENVSXP |
environments |
| 5 | PROMSXP |
promises |
| 6 | LANGSXP |
language objects |
| 7 | SPECIALSXP |
special functions |
| 8 | BUILTINSXP |
builtin functions |
| 9 | CHARSXP |
internal character strings |
| 10 | LGLSXP |
logical vectors |
| 13 | INTSXP |
integer vectors |
| 14 | REALSXP |
numeric vectors |
| 15 | CPLXSXP |
complex vectors |
| 16 | STRSXP |
character vectors |
| 17 | DOTSXP |
dot-dot-dot object |
| 18 | ANYSXP |
make “any” args work |
| 19 | VECSXP |
list (generic vector) |
| 20 | EXPRSXP |
expression vector |
| 21 | BCODESXP |
byte code |
| 22 | EXTPTRSXP |
external pointer |
| 23 | WEAKREFSXP |
weak reference |
| 24 | RAWSXP |
raw vector |
| 25 | OBJSXP |
objects not of simple type |
To extendr, an SEXP is an Robj—it can be anything.
Vector SEXP types
There are a subset of SEXP types that we actually care about—the vector kinds. For the sake of simplicity, we’ll only talk about the numeric vector types.
- Integers are represented by 32 bit integers
i32in Rust. - Doubles are represented by 64 bit floats
f64in Rust.
Scalar types
i32 and f64 do not have a concept of NA or missing. How do we address this?
extendr provides scalar types such as:
RfloatRintRstrRbool
and others.
These scalar types are created using ScalarType::from(). For example:
let my_float = Rfloat::from(3.14);Or, NAs can be created explicitly by using the associated method ScalarType::na()
let my_na = Rfloat::na();- Missingness can also be checked by using the
my_na.is_na()method. - To access the inner type—e.g.
f64ori32we can use the.inner()method.
Wrapper types
To work with vectors, we work with their wrapper types. These are:
DoublesIntegersLogicalsStrings
We should always prefer these types over using Vec<f64> or Vec<i32>.
Cloning an extendr Robj (or other wrapper type) is quite cheap. When cloning an Robj, we are only incrementing a reference count and not performing a full copy like we do when working in R.
Exercise
- Rewrite
gh_encode()to useDoublesfor both thexandyargument - Still return
Vec<String>
Solution
View hint
Use .inner() to access the f64 value from xi and yi.
View solution
#[extendr]
fn gh_encode(x: Doubles, y: Doubles, length: usize) -> Vec<String> {
if length == 0 || length > 12 {
throw_r_error("`length` must be between 1 and 12");
}
x.into_iter()
.zip(y.into_iter())
.map(|(xi, yi)| {
let coord = Coord {
x: xi.inner(),
y: yi.inner(),
};
encode(coord, length).unwrap()
})
.collect::<Vec<_>>()
}