Repository: extendr/extendr
Pull request #87 (2020-12-21 21:58:18)

Better documentation 1
A large collection of examples in the crate documentation all tested.

This should significantly improve the start-up cost for new users.

clauswilke: Actually, the test errors are in the `#[extendr]` macro:

```
error: custom attribute panicked
 --> src/lib.rs:16:1
  |
test src/lib.rs - (line 13) ... FAILED
6 | #[extendr]
test src/lib.rs - (line 31) ... ignored
  | ^^^^^^^^^^
  |
  = help: message: Unable to write R wrapper file to target directory: Os { code: 2, kind: NotFound, message: "No such file or directory" }

error[E0425]: cannot find function `init__fred` in this scope
  --> src/lib.rs:24:7
   |
14 |    fn fred;    
   |       ^^^^ not found in this scope

error: aborting due to 2 previous errors
```
https://github.com/extendr/extendr/pull/87/checks?check_run_id=1591605832#step:9:54

andy-thomason: The test errors are caused by the R wrapper code misfiring in doc tests. We need to separate metadata from
the wrapper code in a subsequent PR.

For now, I'll disable the doc test.
andy-thomason: I've added simpler Robj::from (or r!) conversions to include arrays.

You should now be able to return an array (a fixed size list of values) from a function
or assign it to an object.

```
let my_list : Robj = list!(a = [1, 2, 3], b = [1., 2., 3.]);
```
andy-thomason: Question: should I call f64 values "numeric" or "real".

```
robj.as_real_slice()
vs
robj.as_numeric_slice()
```
andy-thomason: We now have dozens of example doc tests, more consistent names for functions and hopefully a broad spectrum
of support for many more ways of working.

Writing docs has the advantage that you need to fill in the gaps!

Check the docs by running `cargo doc --target-dir /tmp/`

Please prompt me if anything is missing.
andy-thomason: Note the r!() and R!() macros.

The first generates an R object from rust.

The second generates an R object from R source code.

I hope to parameterise the R!() macro in future.

yutannihilation: > Question: should I call f64 values "numeric" or "real".

I guess we should follow R's terminology here; it should be "numeric" if we allow conversion from/to `INTSXP` (and `LGLSXP`?), otherwise "real."

c.f. 
* `isReal()`: https://github.com/wch/r-source/blob/90a3e7d8560e145ef01e1339127c6d67a9deba24/src/include/Rinternals.h#L1821
* `isNumeric()`: https://github.com/wch/r-source/blob/90a3e7d8560e145ef01e1339127c6d67a9deba24/src/include/Rinlinedfuns.h#L961-L972
clauswilke: Overall looks great! One place where I see room for improvement, in particular for macros, is that it would help to add 1--2 sentences explaining in more detail what each macro does and what its arguments are, before showing the examples. Some macros are already there (e.g. `rprint!`) or are fine as is (e.g., `factor!`), but others need more detail.

A couple of specific comments:
- I'm still not sure what the `lang!` macro does. If its purpose is to create a call, then I think this should be stated explicitly: "This macro can be used to create a function call. The first argument is the name of the function (provided as a string), and subsequent arguments are taken as arguments to the function. Arguments can be named, as in R. To evaluate the function call, use `.eval()` or `.eval_blind()`."
- For the `R!` macro, it would be helpful to add something like the following: "The argument to this macro is arbitrary R code. Note that the code does not need to appear in quotes, but you do have to add a semicolon after every R statement. The macro takes the code and evaluates it with `eval()`, so the return value is of type `Result<Robj, AnyError>`."
- I think having an `r!` macro and an `R!` macro is going to be a source of confusion. Maybe the `r!` macro could be renamed into `ro!` or `rob!` or `rfrom!`?
- I agree with @yutannihilation regarding real/numeric.
andy-thomason: The lang! macro creates an R language object which may be a call, and operator or other things.

We could call it "make_call!" and drop the eval_blind() (instead use eval().unwrap())

I'm going to rename the f64 functions to "real" from "numeric" then.
We haven't looked at `complex` in any detail, and I've left this alone for now.
There are a few complex number crates such as `num_complex`.

clauswilke: @andy-thomason One thing that just occurred to me: Both `lang!` and now `call!` probably can't handle function calls to namespaces. Normally you have to locate the namespace first and then you can use `Rf_findFun()` to find the function call in the name space.

As an example, see e.g. here:
https://github.com/clauswilke/sinab/blob/955d38cfb1dc1833433548ff638baf315daa4a4d/src/r_callbacks.c#L18-L19
where the `get_namespace()` function is defined here:
https://github.com/clauswilke/sinab/blob/955d38cfb1dc1833433548ff638baf315daa4a4d/src/r_callbacks.c#L3-L12

Maybe add macros `lang_namespace!` and `call_namespace!` that take a namespace as their second argument? Or modify `lang!` so it can take an R object as input and add a macro `find_fun!` that can locate a function in a namespace?
andy-thomason: I've been avoiding thinking about environments and namespaces so far, but that suggestion is a good one.

I didn't know about `Rf_findFun`, but I'll read about it tomorrow.

I've switched to talking about "integer", "real", "logical" and "raw" for vectors.

We may make all the macros require `unwrap()` or `?` and should write some examples.

I may also add a `Mutex` mechanism, so we can make multi-threaded R calls and run the
tests in parallel.
clauswilke: Yeah, it's pretty straightforward. There's `Rf_findVar()` to find any symbol and `Rf_findFun()` to only find functions. See e.g. here: https://github.com/hadley/r-internals/blob/master/environments.md

It's a good idea to use `Rf_findFun()` to construct a call, so if there's no such function you get a more informative error message.
andy-thomason: @clauswilke We should probably merge this and take the pain of the big change!
clauswilke: Yes, I agree. Better to take several small steps. In particular since the squash commit will compress all of this into one a single commit.

Patch:
diff --git a/extendr-api/examples/R/vectors/src/lib.rs b/extendr-api/examples/R/vectors/src/lib.rs
index 98e258f6f..424d3eae7 100644
--- a/extendr-api/examples/R/vectors/src/lib.rs
+++ b/extendr-api/examples/R/vectors/src/lib.rs
@@ -1,5 +1,6 @@
 
 use extendr_api::*;
+#[doc(hidden)]
 use ndarray::prelude::*;
 
 /// Add two vectors using `ArrayView1` and `ArrayViewMut1`.
@@ -14,7 +15,7 @@ fn add1(a: ArrayView1<f64>, b: ArrayView1<f64>) -> Robj {
     let mut result = rep!(0., len);
 
     // Get a mutable view of the result
-    let mut d = ArrayViewMut1::<f64>::from(result.as_f64_slice_mut().expect("must be a numeric vector"));
+    let mut d = ArrayViewMut1::<f64>::from(result.as_numeric_slice_mut().expect("must be a numeric vector"));
 
     // Use azip to add the vectors.
     // Note: this will panic if the vectors are not the same length.
diff --git a/extendr-api/src/error.rs b/extendr-api/src/error.rs
index a803f01ae..a76770228 100644
--- a/extendr-api/src/error.rs
+++ b/extendr-api/src/error.rs
@@ -6,6 +6,7 @@ use std::os::raw;
 static mut R_ERROR_BUF: Vec<u8> = Vec::new();
 
 /// Throw an R error if a result is an error.
+#[doc(hidden)]
 pub fn unwrap_or_throw<T>(r: Result<T, &'static str>) -> T {
     unsafe {
         match r {
diff --git a/extendr-api/src/args.rs b/extendr-api/src/lang.rs
similarity index 72%
rename from extendr-api/src/args.rs
rename to extendr-api/src/lang.rs
index 77926fc0a..1a430adb7 100644
--- a/extendr-api/src/args.rs
+++ b/extendr-api/src/lang.rs
@@ -6,6 +6,7 @@ use libR_sys::*;
 use crate::robj::Robj;
 
 /// Convert a list of tokens to an array of tuples.
+#[doc(hidden)]
 #[macro_export]
 macro_rules! push_args {
     ($args: expr, $name: ident = $val : expr) => {
@@ -24,6 +25,7 @@ macro_rules! push_args {
     };
 }
 
+#[doc(hidden)]
 #[macro_export]
 macro_rules! args {
     () => {
@@ -38,6 +40,7 @@ macro_rules! args {
     };
 }
 
+#[doc(hidden)]
 pub unsafe fn append_with_name(tail: SEXP, obj: Robj, name: &str) -> SEXP {
     let mut name = Vec::from(name.as_bytes());
     name.push(0);
@@ -50,12 +53,14 @@ pub unsafe fn append_with_name(tail: SEXP, obj: Robj, name: &str) -> SEXP {
     cons
 }
 
+#[doc(hidden)]
 pub unsafe fn append(tail: SEXP, obj: Robj) -> SEXP {
     let cons = Rf_cons(obj.get(), R_NilValue);
     SETCDR(tail, cons);
     cons
 }
 
+#[doc(hidden)]
 pub unsafe fn make_lang(sym: &str) -> Robj {
     let mut name = Vec::from(sym.as_bytes());
     name.push(0);
@@ -64,6 +69,7 @@ pub unsafe fn make_lang(sym: &str) -> Robj {
 }
 
 /// Convert a list of tokens to an array of tuples.
+#[doc(hidden)]
 #[macro_export]
 macro_rules! append_lang {
     ($tail: ident, $name: ident = $val : expr) => {
@@ -82,7 +88,45 @@ macro_rules! append_lang {
     };
 }
 
+/// The call! macro calls an R function with Rust parameters.
+/// Equivalent to `lang!(sym, params).eval()`
+/// This returns a Rust Result.
+///
+/// Example:
+/// ```
+/// use extendr_api::*;
+/// extendr_engine::start_r();
+///
+/// let vec = call!("c", 1.0, 2.0, 3.0).unwrap();
+/// assert_eq!(vec, r!([1., 2., 3.]));
+///
+/// let list = call!("list", a=1, b=2).unwrap();
+/// assert_eq!(list.len(), 2);
+///
+/// let three = call!("+", 1, 2).unwrap();
+/// assert_eq!(three, r!(3));
+/// ```
+#[macro_export]
+macro_rules! call {
+    ($($toks: tt)*) => {
+        lang!($($toks)*).eval()
+    }
+}
+
 /// A macro for constructing R langage objects.
+///
+/// Example:
+/// ```
+/// use extendr_api::*;
+/// extendr_engine::start_r();
+///
+/// let call_to_c = lang!("c", 1., 2., 3.);
+/// let vec = call_to_c.eval().unwrap();
+/// assert_eq!(vec, r!([1., 2., 3.]));
+///
+/// let list = lang!("list", a=1, b=2).eval().unwrap();
+/// assert_eq!(list.len(), 2);
+/// ```
 #[macro_export]
 macro_rules! lang {
     ($sym : expr) => {
@@ -101,28 +145,3 @@ macro_rules! lang {
     };
 }
 
-#[cfg(test)]
-mod tests {
-    //use crate::args;
-    use super::*;
-    use extendr_engine::start_r;
-
-    #[test]
-    fn test_args() {
-        start_r();
-        assert_eq!(Robj::from(1).eval().unwrap(), Robj::from(1));
-        //assert_eq!(Robj::from(Lang("ls")), Robj::from(1));
-        assert_eq!(lang!("+", 1, 1).eval().unwrap(), Robj::from(2));
-        assert_eq!(lang!("+", x = 1, y = 1).eval().unwrap(), Robj::from(2));
-        //assert_eq!(Robj::from(Lang("ls")).and(baseenv()).eval().unwrap(), Robj::from(1));
-        //let plus = Robj::from(Lang("+"));
-        /*assert_eq!(args!(), vec![]);
-        assert_eq!(args!(1), vec![("", 1.into())]);
-        assert_eq!(args!(a=1), vec![("a", 1.into())]);
-        assert_eq!(args!(2, a=1), vec![("", 2.into()), ("a", 1.into())]);
-        assert_eq!(args!(1+1), vec![("", Robj::from(2))]);
-        assert_eq!(args!(1+1, 2), [("", Robj::from(2)), ("", Robj::from(2))]);
-        assert_eq!(args!(a=1+1, b=2), [("a", Robj::from(2)), ("b", Robj::from(2))]);*/
-        //end_r();
-    }
-}
diff --git a/extendr-api/src/lib.rs b/extendr-api/src/lib.rs
index 952178ff3..17027d4aa 100644
--- a/extendr-api/src/lib.rs
+++ b/extendr-api/src/lib.rs
@@ -1,68 +1,147 @@
 //!
-//! extendr - A safe and user friendly R extension interface.
+//! A safe and user friendly R extension interface.
+//!
+//! * Build rust extensions to R.
+//! * Convert R packages to Rust crates.
 //!
 //! This library aims to provide an interface that will be familiar to
 //! first-time users of Rust or indeed any compiled language.
 //!
-//! Anyone who knows the R library should be able to write R extensions.
-//! 
-//! See the [Robj](../robj/enum.Robj.html) struct for much of the content of this crate.
-//! [Robj](../robj/enum.Robj.html) provides a safe wrapper for the R object type.
+//! See [Robj] for much of the content of this crate.
+//! [Robj] provides a safe wrapper for the R object type.
 //! 
-//! This library is just being born, but goals are:
+//! Use attributes and macros to export to R.
+//! ```ignore
+//! use extendr_api::*;
+//! // Export a function or impl to R.
+//! #[extendr]
+//! fn fred(a: i32) -> i32 {
+//!     a + 1
+//! }
 //!
-//! Implement common R functions such as c() and print()
+//! // define exports using extendr_module 
+//! extendr_module! {
+//!    mod mymodule;
+//!    fn fred;    
+//! }
+//! 
+//! ```
 //!
-//! Example:
+//! In R:
 //!
 //! ```ignore
-//! let v = c!(1, 2, 3);
-//! let l = list!(a=1, b=2);
-//! print!(v, l);
+//! result <- fred(1)
 //! ```
 //!
-//! Provide a wrapper for r objects.
+//! [Robj] is a wrapper for R objects.
+//! The r!() and R!() macros let you build R objects
+//! using Rust and R syntax respectively.
+//! ```
+//! use extendr_api::*;        // Put API in scope.
+//! extendr_engine::start_r(); // Start test environment.
 //!
-//! Example:
+//! // An R object with a single string "hello"
+//! let character = r!("hello");
 //!
-//! ```ignore
-//! let s = Robj::from("hello");
-//! let i = Robj::from(1);
-//! let r = Robj::from(1.0);
+//! // An R integer object with a single number 1L.
+//! // Note that in Rust, 1 is an integer and 1.0 is a real.
+//! let integer = r!(1);
+//!
+//! // An R real object with a single number 1.
+//! // Note that in R, 1 is a real and 1L is an integer.
+//! let real = r!(1.0);
+//!
+//! // An R real vector.
+//! let real_vector = r!([1.0, 2.0]);
+//!
+//! // An R function object.
+//! let function = R!(function(x, y) { x + y });
+//!
+//! // A named list using the list! macro.
+//! let list = list!(a = 1, b = 2);
 //! ```
 //!
-//! Provide iterator support for creation and consumption of r vectors.
+//! In Rust, we prefer to use iterators rather than loops.
 //!
-//! Example:
+//! ```
+//! use extendr_api::*;        // Put API in scope.
+//! extendr_engine::start_r(); // Start test environment.
 //!
-//! ```ignore
-//! let res = (1..=100).iter().collect::<Robj>();
-//! for x in res {
-//!     print!(x);
-//! }
+//! // 1 ..= 100 is the same as 1:100
+//! let res = (1 ..= 100).collect_robj();
+//! assert_eq!(res.len(), 100);
+//!
+//! // Rust arrays are zero-indexed so it is more common to use 0 .. 100.
+//! let res = (0 .. 100).collect_robj();
+//! assert_eq!(res.len(), 100);
 //! ```
 //!
-//! Provide a procedural macro to adapt Rust functions to R
+//! To index a vector, first convert it to a slice and then
+//! remember to use 0-based indexing. In Rust, going out of bounds
+//! will cause and error (a panic) unlike C++ which may crash.
+//! ```
+//! use extendr_api::*;        // Put API in scope.
+//! extendr_engine::start_r(); // Start test environment.
 //!
-//! Example:
+//! let vals = r!([1.0, 2.0]);
+//! let slice = vals.as_real_slice().unwrap();
+//! let one = slice[0];
+//! let two = slice[1];
+//! //let error = slice[2];
+//! ```
 //!
-//! ```ignore
-//! #[extendr]
-//! fn fred(a: i32) -> i32 {
-//!     a + 1
-//! }
+//! The [R!] macro lets you embed R code in Rust.
+//!
+//! The [r!] macro converts a rust object to an R object.
+//!
+//! You can call R functions and primitives using the [call!] macro.
 //! ```
 //!
-//! In R:
+//! use extendr_api::*;        // Put API in scope.
+//! extendr_engine::start_r(); // Start test environment.
 //!
-//! ```ignore
+//! // As one R! macro call
+//! let confint1 = R!(confint(lm(weight ~ group - 1, PlantGrowth))).unwrap();
 //!
-//! result <- .Call("fred", 1)
+//! // As many parameterized calls.
+//! let formula = R!(weight ~ group - 1).unwrap();
+//! let plant_growth = R!(PlantGrowth).unwrap();
+//! let model = call!("lm", formula, plant_growth).unwrap();
+//! let confint2 = call!("confint", model).unwrap();
 //!
+//! assert_eq!(confint1.as_real_vector(), confint2.as_real_vector());
 //! ```
 //!
+//! Rust has a concept of "Owned" and "Borrowed" objects.
+//!
+//! Owned objects, such as [Vec] and [String] allocate memory
+//! which is released when the object lifetime ends.
+//!
+//! Borrowed objects such as &[i32] and &str are just pointers
+//! to annother object's memory and can't live longer than the
+//! object they reference.
+//!
+//! Borrowed objects are much faster than owned objects and use less
+//! memory but are used only for temporary access.
+//!
+//! When we take a slice of an R vector, for example, we need the
+//! original R object to be alive or the data will be corrupted.
+//!
+//! ```
+//! use extendr_api::*;        // Put API in scope.
+//! extendr_engine::start_r(); // Start test environment.
+//!
+//! // robj is an "Owned" object that controls the memory allocated.
+//! let robj = r!([1, 2, 3]);
+//!
+//! // slice is a "borrowed" reference to the bytes in robj.
+//! // and cannot live longer than robj.
+//! let slice = robj.as_integer_slice().unwrap();
+//! ```
+
+#![doc(html_logo_url = "https://raw.githubusercontent.com/extendr/extendr/master/extendr-logo-256.png")]
 
-mod args;
+mod lang;
 mod error;
 mod logical;
 mod rmacros;
@@ -72,25 +151,34 @@ mod wrapper;
 #[cfg(feature = "ndarray")]
 mod robj_ndarray;
 
-pub use args::*;
+pub use lang::*;
 pub use error::*;
 pub use rmacros::*;
 pub use robj::*;
 pub use wrapper::*;
+pub use logical::*;
 
 #[cfg(feature = "ndarray")]
 pub use robj_ndarray::*;
 
 pub use extendr_macros::*;
+
+#[doc(hidden)]
 pub use libR_sys::DllInfo;
+
+#[doc(hidden)]
 pub use libR_sys::SEXP;
 
+#[doc(hidden)]
 use libR_sys::*;
+
+#[doc(hidden)]
 use std::ffi::CString;
 
 /// Generic dynamic error type.
 pub type AnyError = Box<dyn std::error::Error + Send + Sync>;
 
+#[doc(hidden)]
 pub struct CallMethod {
     pub call_symbol: std::ffi::CString,
     pub func_ptr: *const u8,
@@ -99,6 +187,7 @@ pub struct CallMethod {
 
 // Internal function used to implement the .Call interface.
 // This is called from the code generated by the #[extendr] attribute.
+#[doc(hidden)]
 pub unsafe fn register_call_methods(info: *mut libR_sys::DllInfo, methods: &[CallMethod]) {
     let mut rmethods: Vec<_> = methods
         .iter()
@@ -128,6 +217,7 @@ pub unsafe fn register_call_methods(info: *mut libR_sys::DllInfo, methods: &[Cal
 
 /// This function is use by the wrapper logic to catch
 /// panics on return.
+#[doc(hidden)]
 pub fn handle_panic<F>(err_str: &str, f: F) -> SEXP
 where
     F : FnOnce() -> SEXP,
@@ -142,14 +232,27 @@ where
     }
 }
 
-// return true if value is an integer NA in R.
-pub fn integer_is_na(val: i32) -> bool {
-    val == std::i32::MIN
+/// Return true if this primitive is NA.
+pub trait IsNA {
+    fn is_na(&self) -> bool;
 }
 
-// return true if value is an numeric NA in R.
-pub fn numeric_is_na(val: f64)  -> bool{
-    unsafe { R_IsNA(val) != 0 }
+impl IsNA for f64 {
+    fn is_na(&self) -> bool {
+        unsafe { R_IsNA(*self) != 0 }
+    }
+}
+
+impl IsNA for i32 {
+    fn is_na(&self) -> bool {
+        *self == std::i32::MIN
+    }
+}
+
+impl IsNA for Bool {
+    fn is_na(&self) -> bool {
+        self.0 == std::i32::MIN
+    }
 }
 
 // pub fn add_function_to_namespace(namespace: &str, fn_name: &str, wrap_name: &str) {
@@ -158,6 +261,7 @@ pub fn numeric_is_na(val: f64)  -> bool{
 //     Robj::eval_string(rcode.as_str()).unwrap();
 // }
 
+#[doc(hidden)]
 pub fn print_r_output<T: Into<Vec<u8>>>(s: T) {
     let cs = CString::new(s).expect("NulError");
     unsafe {
@@ -165,6 +269,7 @@ pub fn print_r_output<T: Into<Vec<u8>>>(s: T) {
     }
 }
 
+#[doc(hidden)]
 pub fn print_r_error<T: Into<Vec<u8>>>(s: T) {
     let cs = CString::new(s).expect("NulError");
     unsafe {
@@ -298,6 +403,7 @@ mod tests {
 
     #[test]
     fn export_test() {
+        extendr_engine::start_r();
         use super::*;
         // Call the exported functions through their generated C wrappers.
         unsafe {
@@ -350,7 +456,7 @@ mod tests {
 
     #[test]
     fn r_output_test() {
-
+        
         // R equivalent
         // > txt_con <- textConnection("test_con", open = "w")
         // > sink(txt_con)
@@ -359,30 +465,14 @@ mod tests {
         // > close(txt_con)
         // > expect_equal(test_con, "Hello world")
         //
-        
-        let con_target_name = "test_con";
-        // `textConnection` outputting to `test_con` R variable
-        let txt_con = 
-            lang!(
-                "textConnection", 
-                Robj::from(con_target_name), 
-                open = Robj::from("w"))
-            .eval().unwrap();
-
-            
-        let txt_con = unsafe { txt_con.get() };
-        // `sink` to connection
-        lang!("sink", txt_con).eval_blind();
+
+        extendr_engine::start_r();
+        let txt_con = R!(textConnection("test_con", open = "w")).unwrap();
+        call!("sink", &txt_con).unwrap();
         rprintln!("Hello world");
-        // `sink` reset
-        lang!("sink").eval_blind();
-        
-        // Retrieve value of `test_con` by evaluating R variable name
-        let result = Robj::eval_string(&con_target_name).unwrap();
-        
-        // Close connection
-        lang!("close", txt_con).eval_blind();
-        
-        assert_eq!(result, Robj::from("Hello world"));
+        call!("sink").unwrap();
+        call!("close", &txt_con).unwrap();
+        let result = R!(test_con).unwrap();
+        assert_eq!(result, r!("Hello world"));
     }
 }
diff --git a/extendr-api/src/logical.rs b/extendr-api/src/logical.rs
index d78ad18bf..6abbc3c8a 100644
--- a/extendr-api/src/logical.rs
+++ b/extendr-api/src/logical.rs
@@ -1,28 +1,28 @@
 
-use super::integer_is_na;
 /// Bool is a wrapper for i32 in the context of an R's tristate boolean.
-#[derive(Debug)]
+/// It can be TRUE, FALSE or NA_LOGICAL.
+#[derive(Debug, PartialEq, Eq)]
 pub struct Bool(pub i32);
 
 impl Bool {
-    // Convert this Bool to a bool. Note NA will be true.
+    /// Convert this Bool to a bool. Note NA will be true.
     pub fn to_bool(&self) -> bool {
         self.0 != 0
     }
 
-    // Convert this construct a Bool from a rust boolean.
+    /// Convert this construct a Bool from a rust boolean.
     pub fn from_bool(val: bool) -> Self {
         Bool(val as i32)
     }
 
-    // Return true if this Bool is NA.
-    pub fn is_na(&self) -> bool {
-        integer_is_na(self.0)
+    /// Test if TRUE
+    pub fn is_true(&self) -> bool {
+        self.0 != 0
     }
 
-    // Construct a NA Bool.
-    pub fn na() -> Bool {
-        Bool(std::i32::MIN)
+    /// Test if FALSE
+    pub fn is_false(&self) -> bool {
+        self.0 == 0
     }
 }
 
@@ -57,9 +57,3 @@ impl From<&Bool> for bool {
         v.0 != 0
     }
 }
-
-impl PartialEq<Bool> for Bool {
-    fn eq(&self, rhs: &Bool) -> bool {
-        self.0 == rhs.0
-    }
-}
diff --git a/extendr-api/src/rmacros.rs b/extendr-api/src/rmacros.rs
index 73f6d3197..b5e3bd34b 100644
--- a/extendr-api/src/rmacros.rs
+++ b/extendr-api/src/rmacros.rs
@@ -2,7 +2,7 @@
 //! rmacros - a set of macros to call actual R functions in a rusty way.
 //!
 
-/// R object encapsulation operator.
+/// Convert a rust expression to an R object.
 ///
 /// Shorthand for Robj::from(x).
 ///
@@ -12,6 +12,19 @@
 /// extendr_engine::start_r();
 /// let fred = r!(1);
 /// assert_eq!(fred, Robj::from(1));
+///
+/// let int_array = r!([1, 2, 3]);
+/// assert_eq!(int_array.len(), 3);
+///
+/// let numeric_array = r!([1., 2., 3.]);
+/// assert_eq!(numeric_array.len(), 3);
+///
+/// let logical_array = r!([true, false, true]);
+/// assert_eq!(logical_array.len(), 3);
+///
+/// let numeric_array_with_na = r!([Some(1.), None, Some(3.)]);
+/// assert_eq!(numeric_array_with_na.len(), 3);
+///
 /// ```
 #[macro_export]
 macro_rules! r {
@@ -20,6 +33,29 @@ macro_rules! r {
     };
 }
 
+/// Call inline R source code from Rust.
+///
+/// ```
+/// use extendr_api::*;
+/// extendr_engine::start_r();
+///
+/// let formula = R!(y ~ z + 1).unwrap();
+/// assert!(formula.inherits("formula"));
+///
+/// // Currently, multiline calls require semicolons as the newlines are lost.
+/// let x = R!({
+///    x <- function() { 1 };
+///    x()
+/// }).unwrap();
+/// assert_eq!(x, r!(1.));
+/// ```
+#[macro_export] 
+macro_rules! R {
+    ($($t:tt)*) => {
+        Robj::eval_string(stringify!($($t)*))
+    };
+}
+
 /// Concatenation operator.
 ///
 /// Example:
@@ -27,15 +63,19 @@ macro_rules! r {
 /// use extendr_api::*;
 /// extendr_engine::start_r();
 /// let fred = c!(1, 2, 3);
-/// assert_eq!(fred, Robj::from(&[1, 2, 3][..]));
+/// assert_eq!(fred, r!([1, 2, 3]));
 /// ```
+/// Note: make sure to use rust syntax for numbers: 1 is integer, 1. is numeric.
+/// For vectors of primitives, prefer to use `r!([1, 2, 3])`.
+///
+/// Panics on error.
 #[macro_export]
 macro_rules! c {
     () => {
-        lang!("c").eval_blind()
+        call!("c").unwrap()
     };
     ($($rest: tt)*) => {
-        lang!("c", $($rest)*).eval_blind()
+        call!("c", $($rest)*).unwrap()
     };
 }
 
@@ -46,12 +86,13 @@ macro_rules! c {
 /// use extendr_api::*;
 /// extendr_engine::start_r();
 /// let fred = rep!(1., 3);
-/// assert_eq!(fred, Robj::from(&[1., 1., 1.][..]));
+/// assert_eq!(fred, r!([1., 1., 1.]));
 /// ```
+/// Note: make sure to use rust syntax for numbers: 1 is integer, 1. is numeric.
 #[macro_export]
 macro_rules! rep {
     ($($rest: tt)*) => {
-        lang!("rep", $($rest)*).eval_blind()
+        call!("rep", $($rest)*).unwrap()
     };
 }
 
@@ -66,7 +107,7 @@ macro_rules! rep {
 #[macro_export]
 macro_rules! read_table {
     ($($rest: tt)*) => {
-        lang!("read.table", $($rest)*).eval()
+        call!("read.table", $($rest)*)
     };
 }
 
@@ -79,13 +120,15 @@ macro_rules! read_table {
 /// let mylist = list!(x=1, y=2);
 /// assert_eq!(mylist, List(&[1.into(), 2.into()]));
 /// ```
+///
+/// Panics on error.
 #[macro_export]
 macro_rules! list {
     () => {
-        lang!("list").eval_blind()
+        call!("list").unwrap()
     };
     ($($rest: tt)*) => {
-        lang!("list", $($rest)*).eval_blind()
+        call!("list", $($rest)*).unwrap()
     };
 }
 
@@ -98,13 +141,15 @@ macro_rules! list {
 /// let mydata = data_frame!(x=1, y=2);
 /// assert_eq!(mydata, List(&[1.into(), 2.into()]));
 /// ```
+///
+/// Panics on error.
 #[macro_export]
 macro_rules! data_frame {
     () => {
-        lang!("data.frame").eval_blind()
+        call!("data.frame").unwrap()
     };
     ($($rest: tt)*) => {
-        lang!("data.frame", $($rest)*).eval_blind()
+        call!("data.frame", $($rest)*).unwrap()
     };
 }
 
@@ -119,10 +164,12 @@ macro_rules! data_frame {
 /// assert_eq!(factor.as_integer_vector().unwrap(), vec![1, 2, 3, 3]);
 /// assert_eq!(factor.str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
 /// ```
+///
+/// Panics on error.
 #[macro_export]
 macro_rules! factor {
     ($($rest: tt)*) => {
-        lang!("factor", $($rest)*).eval_blind()
+        call!("factor", $($rest)*).unwrap()
     };
 }
 
diff --git a/extendr-api/src/robj.rs b/extendr-api/src/robj.rs
deleted file mode 100644
index 9f587c3de..000000000
--- a/extendr-api/src/robj.rs
+++ /dev/null
@@ -1,2420 +0,0 @@
-//! R object handling.
-//!
-//! See. https://cran.r-project.org/doc/manuals/R-exts.html
-//!
-//! Fundamental principals:
-//!
-//! * Any function that can break the protection mechanism is unsafe.
-//! * Users should be able to do almost everything without using libR_sys.
-//! * The interface should be friendly to R users without Rust experience.
-
-use libR_sys::*;
-use std::os::raw;
-
-use crate::logical::*;
-use crate::wrapper::*;
-use crate::AnyError;
-
-use std::collections::HashMap;
-use super::integer_is_na;
-use super::numeric_is_na;
-
-/// Wrapper for an R S-expression pointer (SEXP).
-///
-/// As much as possible we wish to make this object safe (ie. no segfaults).
-///
-/// If you avoid using unsafe functions it is more likely that you will avoid
-/// panics and segfaults. We will take great trouble to ensure that this
-/// is true.
-///
-pub enum Robj {
-    /// This object owns the SEXP and must free it.
-    Owned(SEXP),
-
-    /// This object references a SEXP such as an input parameter.
-    Borrowed(SEXP),
-
-    /// This object references a SEXP owned by libR.
-    Sys(SEXP),
-}
-
-pub const TRUE: bool = true;
-pub const FALSE: bool = false;
-pub const NULL: () = ();
-
-impl Clone for Robj {
-    fn clone(&self) -> Self {
-        self.duplicate()
-    }
-}
-
-impl Default for Robj {
-    fn default() -> Self {
-        Robj::from(())
-    }
-}
-
-/// Trait used for incomming parameter conversion.
-pub trait FromRobj<'a>: Sized {
-    // Convert an incomming Robj from R into a value or an error.
-    fn from_robj(_robj: &'a Robj) -> Result<Self, &'static str> {
-        Err("unable to convert value from R object")
-    }
-}
-
-macro_rules! impl_prim_from_robj {
-    ($t: ty) => {
-        impl<'a> FromRobj<'a> for $t {
-            fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-                if let Some(v) = robj.as_i32_slice() {
-                    match v.len() {
-                        0 => Err("Input must be of length 1. Vector of length zero given."),
-                        1 => if !integer_is_na(v[0]) {
-                                Ok(v[0] as Self)
-                             } else {
-                                Err("Input must not be NA.")
-                             },
-                        _ => Err("Input must be of length 1. Vector of length >1 given."),
-                    }
-                } else if let Some(v) = robj.as_f64_slice() {
-                    match v.len() {
-                        0 => Err("Input must be of length 1. Vector of length zero given."),
-                        1 => if !numeric_is_na(v[0]) {
-                            Ok(v[0] as Self)
-                         } else {
-                            Err("Input must not be NA.")
-                         },
-                        _ => Err("Input must be of length 1. Vector of length >1 given."),
-                    }
-                } else {
-                    Err("unable to convert R object to primitive")
-                }
-            }
-        }
-    };
-}
-
-impl_prim_from_robj!(u8);
-impl_prim_from_robj!(u16);
-impl_prim_from_robj!(u32);
-impl_prim_from_robj!(u64);
-impl_prim_from_robj!(i8);
-impl_prim_from_robj!(i16);
-impl_prim_from_robj!(i32);
-impl_prim_from_robj!(i64);
-impl_prim_from_robj!(f32);
-impl_prim_from_robj!(f64);
-
-impl<'a> FromRobj<'a> for bool {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if let Some(v) = robj.as_logical_slice() {
-            match v.len() {
-                0 => Err("Input must be of length 1. Vector of length zero given."),
-                1 => if !v[0].is_na() {
-                    Ok(v[0].to_bool())
-                 } else {
-                    Err("Input must not be NA.")
-                 },
-            _ => Err("Input must be of length 1. Vector of length >1 given."),
-            }
-        } else {
-            Err("Not a logical object.")
-        }
-    }
-}
-
-impl<'a> FromRobj<'a> for &'a str {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if robj.is_na() {
-            Err("Input must not be NA.")
-        } else if let Some(s) = robj.as_str() {
-            Ok(s)
-        } else {
-            Err("Not a string object.")
-        }
-    }
-}
-
-impl<'a> FromRobj<'a> for String {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if robj.is_na() {
-            Err("Input must not be NA.")
-        } else if let Some(s) = robj.as_str() {
-            Ok(s.to_string())
-        } else {
-            Err("not a string object")
-        }
-    }
-}
-
-impl<'a> FromRobj<'a> for Vec<i32> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if let Some(v) = robj.as_i32_slice() {
-            Ok(Vec::from(v))
-        } else {
-            Err("not an integer or logical vector")
-        }
-    }
-}
-
-impl<'a> FromRobj<'a> for Vec<f64> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if let Some(v) = robj.as_f64_slice() {
-            Ok(Vec::from(v))
-        } else {
-            Err("not a floating point vector")
-        }
-    }
-}
-
-macro_rules! impl_iter_from_robj {
-    ($t: ty, $iter_fn: ident, $msg: expr) => {
-        impl<'a> FromRobj<'a> for $t {
-            fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-                if let Some(v) = robj.$iter_fn() {
-                    Ok(v)
-                } else {
-                    Err($msg)
-                }
-            }
-        }
-    };
-}
-
-impl_iter_from_robj!(StrIter, str_iter, "Not a character vector.");
-impl_iter_from_robj!(VecIter, list_iter, "Not a list.");
-impl_iter_from_robj!(IntegerIter<'a>, integer_iter, "Not an integer vector.");
-impl_iter_from_robj!(NumericIter<'a>, numeric_iter, "Not a numeric vector.");
-impl_iter_from_robj!(LogicalIter<'a>, logical_iter, "Not a logical vector.");
-
-/// Pass-through Robj conversion.
-impl<'a> FromRobj<'a> for Robj {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        Ok(unsafe { new_borrowed(robj.get()) })
-    }
-}
-
-impl<'a> FromRobj<'a> for HashMap<String, Robj> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if let Some(iter) = robj.named_list_iter() {
-            Ok(iter.map(|(k, v)| (k.to_string(), v.to_owned())).collect::<HashMap<String, Robj>>())
-        } else {
-            Err("expected a list")
-        }
-    }
-}
-
-impl<'a> FromRobj<'a> for HashMap<&str, Robj> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if let Some(iter) = robj.named_list_iter() {
-            Ok(iter.map(|(k, v)| (k, v)).collect::<HashMap<&str, Robj>>())
-        } else {
-            Err("expected a list")
-        }
-    }
-}
-
-// NA-sensitive integer input handling
-impl<'a> FromRobj<'a> for Option<i32> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if robj.is_na() {
-            Ok(None)
-        } else if let Some(val) = robj.as_integer() {
-            Ok(Some(val))
-        } else {
-            Err("expected an integer scalar")
-        }
-    }
-}
-
-// NA-sensitive logical input handling
-impl<'a> FromRobj<'a> for Option<bool> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if robj.is_na() {
-            Ok(None)
-        } else if let Some(val) = robj.as_logical() {
-            Ok(Some(val))
-        } else {
-            Err("expected a logical scalar")
-        }
-    }
-}
-
-// NA-sensitive numeric input handling
-impl<'a> FromRobj<'a> for Option<f64> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if robj.is_na() {
-            Ok(None)
-        } else if let Some(val) = robj.as_numeric() {
-            Ok(Some(val))
-        } else {
-            Err("expected a numeric scalar")
-        }
-    }
-}
-
-// NA-sensitive string input handling
-impl<'a> FromRobj<'a> for Option<&'a str> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if robj.is_na() {
-            Ok(None)
-        } else if let Some(val) = robj.as_str() {
-            Ok(Some(val))
-        } else {
-            Err("expected a character scalar")
-        }
-    }
-}
-
-// NA-sensitive string input handling
-impl<'a> FromRobj<'a> for Option<String> {
-    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
-        if robj.is_na() {
-            Ok(None)
-        } else if let Some(val) = robj.as_str() {
-            Ok(Some(val.to_string()))
-        } else {
-            Err("expected a character scalar")
-        }
-    }
-}
-
-impl Robj {
-    /// Get a copy of the underlying SEXP.
-    /// Note: this is unsafe.
-    pub unsafe fn get(&self) -> SEXP {
-        match self {
-            Robj::Owned(sexp) => *sexp,
-            Robj::Borrowed(sexp) => *sexp,
-            Robj::Sys(sexp) => *sexp,
-        }
-    }
-
-    /// Get a copy of the underlying SEXP for mutable types.
-    /// This is valid only for owned objects as we are not
-    /// permitted to modify parameters or system objects.
-    pub unsafe fn get_mut(&mut self) -> Option<SEXP> {
-        match self {
-            Robj::Owned(sexp) => Some(*sexp),
-            Robj::Borrowed(_) => None,
-            Robj::Sys(_) => None,
-        }
-    }
-
-    /// Get the XXXSXP type of the object.
-    pub fn sexptype(&self) -> u32 {
-        unsafe { TYPEOF(self.get()) as u32 }
-    }
-
-    /// Get the extended length of the object.
-    pub fn len(&self) -> usize {
-        unsafe { Rf_xlength(self.get()) as usize }
-    }
-
-    /// Is this object is an NA?
-    /// Works for character, integer and numeric types.
-    pub fn is_na(&self) -> bool {
-        if self.len() != 1 {
-            false
-        } else {
-            unsafe {
-                let sexp = self.get();
-                match self.sexptype() {
-                    STRSXP => STRING_ELT(sexp, 0) == libR_sys::R_NaString,
-                    INTSXP => *(INTEGER(sexp)) == libR_sys::R_NaInt,
-                    LGLSXP => *(LOGICAL(sexp)) == libR_sys::R_NaInt,
-                    REALSXP => R_IsNA(*(REAL(sexp))) != 0,
-                    _ => false,
-                }
-            }
-        }
-    }
-
-    /// Get a read-only reference to the content of an integer or logical vector.
-    pub fn as_i32_slice(&self) -> Option<&[i32]> {
-        self.as_typed_slice()
-    }
-
-    /// Get a read-only reference to the content of an integer vector.
-    pub fn as_integer_slice(&self) -> Option<&[i32]> {
-        self.as_typed_slice()
-    }
-
-    /// Get an iterator over integer elements of this slice.
-    pub fn integer_iter(&self) -> Option<IntegerIter> {
-        if let Some(slice) = self.as_integer_slice() {
-            Some(slice.iter())
-        } else {
-            None
-        }
-    }
-
-    /// Get a Vec<i32> copied from the object.
-    pub fn as_integer_vector(&self) -> Option<Vec<i32>> {
-        if let Some(value) = self.as_integer_slice() {
-            Some(value.iter().cloned().collect::<Vec<_>>())
-        } else {
-            None
-        }
-    }
-
-    /// Get a read-only reference to the content of an integer or logical vector.
-    pub fn as_logical_slice(&self) -> Option<&[Bool]> {
-        self.as_typed_slice()
-    }
-
-    /// Get a Vec<bool> copied from the object.
-    pub fn as_logical_vector(&self) -> Option<Vec<bool>> {
-        if let Some(value) = self.as_logical_slice() {
-            Some(value.iter().map(|x| x.clone().into()).collect::<Vec<_>>())
-        } else {
-            None
-        }
-    }
-
-    /// Get an iterator over logical elements of this slice.
-    pub fn logical_iter(&self) -> Option<LogicalIter> {
-        if let Some(slice) = self.as_logical_slice() {
-            Some(slice.iter())
-        } else {
-            None
-        }
-    }
-
-    /// Get a read-only reference to the content of a double vector.
-    pub fn as_f64_slice(&self) -> Option<&[f64]> {
-        self.as_typed_slice()
-    }
-
-    /// Get a read-only reference to the content of a double vector.
-    pub fn as_numeric_slice(&self) -> Option<&[f64]> {
-        self.as_typed_slice()
-    }
-
-    /// Get an iterator over numeric elements of this slice.
-    pub fn numeric_iter(&self) -> Option<NumericIter> {
-        if let Some(slice) = self.as_numeric_slice() {
-            Some(slice.iter())
-        } else {
-            None
-        }
-    }
-
-    /// Get a Vec<f64> copied from the object.
-    pub fn as_numeric_vector(&self) -> Option<Vec<f64>> {
-        if let Some(value) = self.as_numeric_slice() {
-            Some(value.iter().cloned().collect::<Vec<_>>())
-        } else {
-            None
-        }
-    }
-
-    /// Get a read-only reference to the content of an integer or logical vector.
-    pub fn as_u8_slice(&self) -> Option<&[u8]> {
-        self.as_typed_slice()
-    }
-
-    /// Get a read-only reference to the content of an integer or logical vector.
-    pub fn as_raw_slice(&self) -> Option<&[u8]> {
-        self.as_typed_slice()
-    }
-
-    /// Get a read-write reference to the content of an integer or logical vector.
-    pub fn as_i32_slice_mut(&mut self) -> Option<&mut [i32]> {
-        self.as_typed_slice_mut()
-    }
-
-    /// Get a read-write reference to the content of a double vector.
-    pub fn as_f64_slice_mut(&mut self) -> Option<&mut [f64]> {
-        self.as_typed_slice_mut()
-    }
-
-    /// Get a read-write reference to the content of an integer or logical vector.
-    pub fn as_u8_slice_mut(&mut self) -> Option<&mut [u8]> {
-        self.as_typed_slice_mut()
-    }
-
-    /// Get an iterator over a pairlist.
-    pub fn pairlist_iter(&self) -> Option<ListIter> {
-        match self.sexptype() {
-            LISTSXP | LANGSXP | DOTSXP => unsafe {
-                Some(ListIter {
-                    list_elem: self.get(),
-                })
-            },
-            _ => None,
-        }
-    }
-
-    /// Get an iterator over an unnamed list.
-    pub fn list_iter(&self) -> Option<VecIter> {
-        match self.sexptype() {
-            VECSXP | EXPRSXP | WEAKREFSXP => unsafe {
-                Some(VecIter {
-                    vector: self.get(),
-                    i: 0,
-                    len: self.len(),
-                })
-            },
-            _ => None,
-        }
-    }
-
-    /// Get an iterator over a string vector.
-    /// Returns None if the object is not a string vector
-    /// but works for factors.
-    ///
-    /// ```
-    /// use extendr_api::*;
-    ///
-    /// extendr_engine::start_r();
-    ///
-    /// let obj = Robj::from(vec!["a", "b", "c"]);
-    /// assert_eq!(obj.str_iter().unwrap().collect::<Vec<_>>(), vec!["a", "b", "c"]);
-    /// 
-    /// let factor = factor!(vec!["abcd", "def", "fg", "fg"]);
-    /// assert_eq!(factor.levels().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg"]);
-    /// assert_eq!(factor.as_integer_vector().unwrap(), vec![1, 2, 3, 3]);
-    /// assert_eq!(factor.str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
-    /// assert_eq!(factor.str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
-    /// ```
-    pub fn str_iter(&self) -> Option<StrIter> {
-        let i = 0;
-        let len = self.len();
-        match self.sexptype() {
-            STRSXP => unsafe {
-                let vector = self.get();
-                Some(StrIter {vector, i, len, levels: R_NilValue})
-            },
-            INTSXP => unsafe {
-                let vector = self.get();
-                let levels = self.getAttrib(&Robj::levelsSymbol());
-                if self.isFactor() && levels.sexptype() == STRSXP {
-                    Some(StrIter {vector, i, len, levels: levels.get()})
-                } else {
-                    None
-                }
-            },
-            _ => None,
-        }
-    }
-
-    /// Get a vector of owned strings.
-    /// Owned strings have long lifetimes, but are much slower than references.
-    /// ```
-    ///    use extendr_api::*;
-    ///    extendr_engine::start_r();
-    ///    let robj1 = Robj::from("xyz");
-    ///    assert_eq!(robj1.as_string_vector(), Some(vec!["xyz".to_string()]));
-    ///    let robj2 = Robj::from(1);
-    ///    assert_eq!(robj2.as_string_vector(), None);
-    /// ```
-    pub fn as_string_vector(&self) -> Option<Vec<String>> {
-        if let Some(iter) = self.str_iter() {
-            Some(iter.map(str::to_string).collect())
-        } else {
-            None
-        }
-    }
-
-    /// Get a vector of string references.
-    /// String references (&str) are faster, but have short lifetimes.
-    /// ```
-    ///    use extendr_api::*;
-    ///    extendr_engine::start_r();
-    ///    let robj1 = Robj::from("xyz");
-    ///    assert_eq!(robj1.as_str_vector(), Some(vec!["xyz"]));
-    ///    let robj2 = Robj::from(1);
-    ///    assert_eq!(robj2.as_str_vector(), None);
-    /// ```
-    pub fn as_str_vector(&self) -> Option<Vec<&str>> {
-        if let Some(iter) = self.str_iter() {
-            Some(iter.collect())
-        } else {
-            None
-        }
-    }
-
-    /// Get a read-only reference to a char, symbol or string type.
-    pub fn as_str(&self) -> Option<&str> {
-        unsafe {
-            match self.sexptype() {
-                STRSXP => {
-                    if self.len() != 1 {
-                        None
-                    } else {
-                        Some(to_str(R_CHAR(STRING_ELT(self.get(), 0)) as *const u8))
-                    }
-                }
-                CHARSXP => Some(to_str(R_CHAR(self.get()) as *const u8)),
-                SYMSXP => Some(to_str(R_CHAR(PRINTNAME(self.get())) as *const u8)),
-                _ => None,
-            }
-        }
-    }
-
-    /// Get a scalar integer.
-    pub fn as_i32(&self) -> Option<i32> {
-        match self.as_i32_slice() {
-            Some(slice) if slice.len() == 1 => Some(slice[0]),
-            _ => None
-        }
-    }
-
-    /// Get a scalar integer.
-    pub fn as_integer(&self) -> Option<i32> {
-        self.as_i32()
-    }
-
-    /// Get a scalar real.
-    pub fn as_f64(&self) -> Option<f64> {
-        match self.as_f64_slice() {
-            Some(slice) if slice.len() == 1 => Some(slice[0]),
-            _ => None
-        }
-    }
-
-    /// Get a scalar real.
-    pub fn as_numeric(&self) -> Option<f64> {
-        self.as_f64()
-    }
-
-    /// Get a scalar boolean.
-    pub fn as_bool(&self) -> Option<bool> {
-        match self.as_logical_slice() {
-            Some(slice) if slice.len() == 1 => Some(slice[0].into()),
-            _ => None
-        }
-    }
-
-    /// Get a scalar boolean.
-    pub fn as_logical(&self) -> Option<bool> {
-        match self.as_logical_slice() {
-            Some(slice) if slice.len() == 1 => Some(slice[0].into()),
-            _ => None
-        }
-    }
-
-    /// Evaluate the expression and return an error or an R object.
-    pub fn eval(&self) -> Result<Robj, AnyError> {
-        unsafe {
-            let mut error: raw::c_int = 0;
-            let res = R_tryEval(self.get(), R_GlobalEnv, &mut error as *mut raw::c_int);
-            if error != 0 {
-                Err(AnyError::from("R eval error"))
-            } else {
-                Ok(Robj::from(res))
-            }
-        }
-    }
-
-    /// Evaluate the expression and return NULL or an R object.
-    pub fn eval_blind(&self) -> Robj {
-        unsafe {
-            let mut error: raw::c_int = 0;
-            let res = R_tryEval(self.get(), R_GlobalEnv, &mut error as *mut raw::c_int);
-            if error != 0 {
-                Robj::from(())
-            } else {
-                Robj::from(res)
-            }
-        }
-    }
-
-    /// Parse a string into an R executable object
-    pub fn parse(code: &str) -> Result<Robj, AnyError> {
-        unsafe {
-            use libR_sys::*;
-            let mut status = 0_u32;
-            let status_ptr = &mut status as *mut u32;
-            let code: Robj = code.into();
-            let parsed = Robj::from(R_ParseVector(code.get(), -1, status_ptr, R_NilValue));
-            match status {
-                1 => Ok(parsed),
-                _ => Err(AnyError::from("parse_error")),
-            }
-        }
-    }
-
-    /// Parse a string into an R executable object and run it.
-    pub fn eval_string(code: &str) -> Result<Robj, AnyError> {
-        let expr = Robj::parse(code)?;
-        let mut res = Robj::from(());
-        if let Some(iter) = expr.list_iter() {
-            for lang in iter {
-                res = lang.eval()?;
-            }
-        }
-        Ok(res)
-    }
-
-    /// Unprotect an object - assumes a transfer of ownership.
-    /// This is unsafe because the object pointer may be left dangling.
-    pub unsafe fn unprotected(self) -> Robj {
-        match self {
-            Robj::Owned(sexp) => {
-                R_ReleaseObject(sexp);
-                Robj::Borrowed(sexp)
-            }
-            _ => self,
-        }
-    }
-
-    /// Return true if the object is owned by this wrapper.
-    /// If so, it will be released when the wrapper drops.
-    pub fn is_owned(&self) -> bool {
-        match self {
-            Robj::Owned(_) => true,
-            _ => false,
-        }
-    }
-
-    // Convert the Robj to an owned one.
-    pub fn to_owned(self) -> Robj {
-        match self {
-            Robj::Owned(_) => self,
-            _ => unsafe { new_owned(self.get()) }
-        }
-    }
-}
-
-pub trait AsTypedSlice<T> {
-    fn as_typed_slice(&self) -> Option<&[T]> {
-        None
-    }
-    fn as_typed_slice_mut(&mut self) -> Option<&mut [T]> {
-        None
-    }
-}
-
-macro_rules! make_typed_slice {
-    ($type: ty, $fn: tt, $($sexp: tt),* ) => {
-        impl AsTypedSlice<$type> for Robj {
-            fn as_typed_slice(&self) -> Option<&[$type]> {
-                match self.sexptype() {
-                    $( $sexp )|* => {
-                        unsafe {
-                            let ptr = $fn(self.get()) as *const $type;
-                            Some(std::slice::from_raw_parts(ptr, self.len()))
-                        }
-                    }
-                    _ => None
-                }
-            }
-
-            fn as_typed_slice_mut(&mut self) -> Option<&mut [$type]> {
-                match self.sexptype() {
-                    $( $sexp )|* => {
-                        unsafe {
-                            let ptr = $fn(self.get()) as *mut $type;
-                            Some(std::slice::from_raw_parts_mut(ptr, self.len()))
-                        }
-                    }
-                    _ => None
-                }
-            }
-        }
-    }
-}
-
-make_typed_slice!(Bool, INTEGER, LGLSXP);
-make_typed_slice!(i32, INTEGER, INTSXP);
-make_typed_slice!(f64, REAL, REALSXP);
-make_typed_slice!(u8, RAW, RAWSXP);
-
-// These are helper functions which give access to common properties of R objects.
-#[allow(non_snake_case)]
-impl Robj {
-    /// Get the names attribute as a string iterator if one exists.
-    pub fn names(&self) -> Option<StrIter> {
-        self.getAttrib(&Robj::namesSymbol()).str_iter()
-    }
-
-    /// Return an iterator over names and values of a list if they exist.
-    pub fn named_list_iter(&self) -> Option<NamedListIter> {
-        if let Some(names) = self.names() {
-            if let Some(values) = self.list_iter() {
-                return Some(names.zip(values));
-            }
-        }
-        None
-    }
-
-    /// Get the class attribute as a string iterator if one exists.
-    pub fn class(&self) -> Option<StrIter> {
-        self.getAttrib(&Robj::classSymbol()).str_iter()
-    }
-
-    /// Return true if this class inherits this class.
-    pub fn inherits(&self, classname: &str) -> bool {
-        if let Some(mut iter) = self.class() {
-            iter.find(|&n| n == classname).is_some()
-        } else {
-            false
-        }
-    }
-
-    /// Get the levels attribute as a string iterator if one exists.
-    pub fn levels(&self) -> Option<StrIter> {
-        self.getAttrib(&Robj::levelsSymbol()).str_iter()
-    }
-}
-
-///////////////////////////////////////////////////////////////
-/// The following impls wrap specific Rinternals.h symbols.
-///
-#[allow(non_snake_case)]
-impl Robj {
-    /// The "global" environment
-    pub fn globalEnv() -> Robj {
-        unsafe { new_sys(R_GlobalEnv) }
-    }
-    /// An empty environment at the root of the environment tree
-    pub fn emptyEnv() -> Robj {
-        unsafe { new_sys(R_EmptyEnv) }
-    }
-    /// The base environment; formerly R_NilValue
-    pub fn baseEnv() -> Robj {
-        unsafe { new_sys(R_BaseEnv) }
-    }
-    /// The (fake) namespace for base
-    pub fn baseNamespace() -> Robj {
-        unsafe { new_sys(R_BaseNamespace) }
-    }
-    /// for registered namespaces
-    pub fn namespaceRegistry() -> Robj {
-        unsafe { new_sys(R_NamespaceRegistry) }
-    }
-    /// Current srcref, for debuggers
-    pub fn srcref() -> Robj {
-        unsafe { new_sys(R_Srcref) }
-    }
-    /// The nil object
-    pub fn nilValue() -> Robj {
-        unsafe { new_sys(R_NilValue) }
-    }
-    /// Unbound marker
-    pub fn unboundValue() -> Robj {
-        unsafe { new_sys(R_UnboundValue) }
-    }
-    /// Missing argument marker
-    pub fn missingArg() -> Robj {
-        unsafe { new_sys(R_MissingArg) }
-    }
-
-    /* Not supported by older R versions.
-    /// To be found in BC interp. state (marker)
-    pub fn inBCInterpreter() -> Robj { unsafe { new_sys(R_InBCInterpreter) }}
-    /// Use current expression (marker)
-    pub fn currentExpression() -> Robj { unsafe { new_sys(R_CurrentExpression) }}
-    /// character"
-    pub fn asCharacterSymbol() -> Robj { unsafe { new_sys(R_AsCharacterSymbol) }}
-    */
-
-    /// "base"
-    pub fn baseSymbol() -> Robj {
-        unsafe { new_sys(R_BaseSymbol) }
-    }
-    /// "{"
-    pub fn braceSymbol() -> Robj {
-        unsafe { new_sys(R_BraceSymbol) }
-    }
-    /// "[["
-    pub fn bracket2Symbol() -> Robj {
-        unsafe { new_sys(R_Bracket2Symbol) }
-    }
-    /// "["
-    pub fn bracketSymbol() -> Robj {
-        unsafe { new_sys(R_BracketSymbol) }
-    }
-    /// "class"
-    pub fn classSymbol() -> Robj {
-        unsafe { new_sys(R_ClassSymbol) }
-    }
-    /// ".Device"
-    pub fn deviceSymbol() -> Robj {
-        unsafe { new_sys(R_DeviceSymbol) }
-    }
-    /// "dimnames"
-    pub fn dimNamesSymbol() -> Robj {
-        unsafe { new_sys(R_DimNamesSymbol) }
-    }
-    /// "dim"
-    pub fn dimSymbol() -> Robj {
-        unsafe { new_sys(R_DimSymbol) }
-    }
-    /// "$"
-    pub fn dollarSymbol() -> Robj {
-        unsafe { new_sys(R_DollarSymbol) }
-    }
-    /// "..."
-    pub fn dotsSymbol() -> Robj {
-        unsafe { new_sys(R_DotsSymbol) }
-    }
-    ///     pub fn dropSymbol() -> Robj { unsafe { new_sys(R_DropSymbol) }}"drop"
-    pub fn doubleColonSymbol() -> Robj {
-        unsafe { new_sys(R_DoubleColonSymbol) }
-    } //
-    /// ".Last.value"
-    pub fn lastvalueSymbol() -> Robj {
-        unsafe { new_sys(R_LastvalueSymbol) }
-    }
-    /// "levels"
-    pub fn levelsSymbol() -> Robj {
-        unsafe { new_sys(R_LevelsSymbol) }
-    }
-    /// "mode"
-    pub fn modeSymbol() -> Robj {
-        unsafe { new_sys(R_ModeSymbol) }
-    }
-    /// "na.rm"
-    pub fn naRmSymbol() -> Robj {
-        unsafe { new_sys(R_NaRmSymbol) }
-    }
-    /// "name"
-    pub fn nameSymbol() -> Robj {
-        unsafe { new_sys(R_NameSymbol) }
-    }
-    /// "names"
-    pub fn namesSymbol() -> Robj {
-        unsafe { new_sys(R_NamesSymbol) }
-    }
-    /// _NAMESPACE__."
-    pub fn namespaceEnvSymbol() -> Robj {
-        unsafe { new_sys(R_NamespaceEnvSymbol) }
-    }
-    /// "package"
-    pub fn packageSymbol() -> Robj {
-        unsafe { new_sys(R_PackageSymbol) }
-    }
-    /// "previous"
-    pub fn previousSymbol() -> Robj {
-        unsafe { new_sys(R_PreviousSymbol) }
-    }
-    /// "quote"
-    pub fn quoteSymbol() -> Robj {
-        unsafe { new_sys(R_QuoteSymbol) }
-    }
-    /// "row.names"
-    pub fn rowNamesSymbol() -> Robj {
-        unsafe { new_sys(R_RowNamesSymbol) }
-    }
-    /// ".Random.seed"
-    pub fn seedsSymbol() -> Robj {
-        unsafe { new_sys(R_SeedsSymbol) }
-    }
-    /// "sort.list"
-    pub fn sortListSymbol() -> Robj {
-        unsafe { new_sys(R_SortListSymbol) }
-    }
-    /// "source"
-    pub fn sourceSymbol() -> Robj {
-        unsafe { new_sys(R_SourceSymbol) }
-    }
-    /// "spec"
-    pub fn specSymbol() -> Robj {
-        unsafe { new_sys(R_SpecSymbol) }
-    }
-    /// "tsp"
-    pub fn tspSymbol() -> Robj {
-        unsafe { new_sys(R_TspSymbol) }
-    }
-    /// ":::"
-    pub fn tripleColonSymbol() -> Robj {
-        unsafe { new_sys(R_TripleColonSymbol) }
-    }
-    /// ".defined"
-    pub fn dot_defined() -> Robj {
-        unsafe { new_sys(R_dot_defined) }
-    }
-    /// ".Method"
-    pub fn dot_Method() -> Robj {
-        unsafe { new_sys(R_dot_Method) }
-    }
-    /// "packageName"
-    pub fn dot_packageName() -> Robj {
-        unsafe { new_sys(R_dot_packageName) }
-    } //
-    /// ".target"
-    pub fn dot_target() -> Robj {
-        unsafe { new_sys(R_dot_target) }
-    }
-    /* fix version issues.
-    /// ".Generic"
-    pub fn dot_Generic() -> Robj { unsafe { new_sys(R_dot_Generic) }}
-    */
-    /// NA_STRING as a CHARSXP
-    pub fn naString() -> Robj {
-        unsafe { new_sys(R_NaString) }
-    }
-    /// "" as a CHARSXP
-    pub fn blankString() -> Robj {
-        unsafe { new_sys(R_BlankString) }
-    }
-    /// as a STRSXP
-    pub fn blankScalarString() -> Robj {
-        unsafe { new_sys(R_BlankScalarString) }
-    }
-}
-
-///////////////////////////////////////////////////////////////
-/// The following impls wrap specific Rinternals.h functions.
-///
-#[allow(non_snake_case)]
-impl Robj {
-    /// Return true if this is the null object.
-    pub fn isNull(&self) -> bool {
-        unsafe { Rf_isNull(self.get()) != 0 }
-    }
-
-    /// Return true if this is a symbol.
-    pub fn isSymbol(&self) -> bool {
-        unsafe { Rf_isSymbol(self.get()) != 0 }
-    }
-
-    /// Return true if this is a boolean (logical) vector
-    pub fn isLogical(&self) -> bool {
-        unsafe { Rf_isLogical(self.get()) != 0 }
-    }
-
-    /// Return true if this is a real (f64) vector.
-    pub fn isReal(&self) -> bool {
-        unsafe { Rf_isReal(self.get()) != 0 }
-    }
-
-    /// Return true if this is a complex vector.
-    pub fn isComplex(&self) -> bool {
-        unsafe { Rf_isComplex(self.get()) != 0 }
-    }
-
-    /// Return true if this is an expression.
-    pub fn isExpression(&self) -> bool {
-        unsafe { Rf_isExpression(self.get()) != 0 }
-    }
-
-    /// Return true if this is an environment.
-    pub fn isEnvironment(&self) -> bool {
-        unsafe { Rf_isEnvironment(self.get()) != 0 }
-    }
-
-    /// Return true if this is a string.
-    pub fn isString(&self) -> bool {
-        unsafe { Rf_isString(self.get()) != 0 }
-    }
-
-    /// Return true if this is an object.
-    pub fn isObject(&self) -> bool {
-        unsafe { Rf_isObject(self.get()) != 0 }
-    }
-
-    /// Get the source ref.
-    pub fn getCurrentSrcref(val: i32) -> Robj {
-        unsafe { new_owned(R_GetCurrentSrcref(val as raw::c_int)) }
-    }
-
-    /// Get the source filename.
-    pub fn getSrcFilename(&self) -> Robj {
-        unsafe { new_owned(R_GetSrcFilename(self.get())) }
-    }
-
-    /// Convert to a string vector.
-    pub fn asChar(&self) -> Robj {
-        unsafe { new_owned(Rf_asChar(self.get())) }
-    }
-
-    /// Convert to vectors of many kinds.
-    pub fn coerceVector(&self, sexptype: u32) -> Robj {
-        unsafe { new_owned(Rf_coerceVector(self.get(), sexptype as SEXPTYPE)) }
-    }
-
-    /// Convert a pairlist (LISTSXP) to a vector list (VECSXP).
-    pub fn pairToVectorList(&self) -> Robj {
-        unsafe { new_owned(Rf_PairToVectorList(self.get())) }
-    }
-
-    /// Convert a vector list (VECSXP) to a pair list (LISTSXP)
-    pub fn vectorToPairList(&self) -> Robj {
-        unsafe { new_owned(Rf_VectorToPairList(self.get())) }
-    }
-
-    /// Convert a factor to a string vector.
-    pub fn asCharacterFactor(&self) -> Robj {
-        unsafe { new_owned(Rf_asCharacterFactor(self.get())) }
-    }
-
-    /// Get a scalar boolean value
-    pub fn asLogical(&self) -> bool {
-        unsafe { Rf_asLogical(self.get()) != 0 }
-    }
-
-    /// Get a scalar 32 bit integer value
-    pub fn asInteger(&self) -> i32 {
-        unsafe { Rf_asInteger(self.get()) as i32 }
-    }
-
-    /// Get a 64 bit double value
-    pub fn asReal(&self) -> f64 {
-        unsafe { Rf_asReal(self.get()) as f64 }
-    }
-
-    /// Allocate a matrix object (see NumericMatrix etc.)
-    pub fn allocMatrix(sexptype: SEXPTYPE, rows: i32, cols: i32) -> Robj {
-        unsafe { new_owned(Rf_allocMatrix(sexptype, rows, cols)) }
-    }
-
-    /* TODO:
-    int Rf_asLogical2(SEXP x, int checking, SEXP call, SEXP rho);
-    Rcomplex Rf_asComplex(SEXP x);
-    void Rf_addMissingVarsToNewEnv(SEXP, SEXP);
-    SEXP Rf_alloc3DArray(SEXPTYPE, int, int, int);
-    SEXP Rf_allocArray(SEXPTYPE, SEXP);
-    SEXP Rf_allocFormalsList2(SEXP sym1, SEXP sym2);
-    SEXP Rf_allocFormalsList3(SEXP sym1, SEXP sym2, SEXP sym3);
-    SEXP Rf_allocFormalsList4(SEXP sym1, SEXP sym2, SEXP sym3, SEXP sym4);
-    SEXP Rf_allocFormalsList5(SEXP sym1, SEXP sym2, SEXP sym3, SEXP sym4, SEXP sym5);
-    SEXP Rf_allocFormalsList6(SEXP sym1, SEXP sym2, SEXP sym3, SEXP sym4, SEXP sym5, SEXP sym6);
-    SEXP Rf_allocList(int);
-    SEXP Rf_allocS4Object(void);
-    SEXP Rf_allocSExp(SEXPTYPE);
-    SEXP Rf_allocVector3(SEXPTYPE, R_xlen_t, R_allocator_t*);
-    R_xlen_t Rf_any_duplicated(SEXP x, Rboolean from_last);
-    R_xlen_t Rf_any_duplicated3(SEXP x, SEXP incomp, Rboolean from_last);
-    SEXP Rf_applyClosure(SEXP, SEXP, SEXP, SEXP, SEXP);
-    SEXP Rf_arraySubscript(int, SEXP, SEXP, SEXP (*)(SEXP,SEXP), SEXP (*)(SEXP, int), SEXP);
-    SEXP Rf_classgets(SEXP, SEXP);
-    SEXP Rf_cons(SEXP, SEXP);
-    SEXP Rf_fixSubset3Args(SEXP, SEXP, SEXP, SEXP*);
-    void Rf_copyMatrix(SEXP, SEXP, Rboolean);
-    void Rf_copyListMatrix(SEXP, SEXP, Rboolean);
-    void Rf_copyMostAttrib(SEXP, SEXP);
-    void Rf_copyVector(SEXP, SEXP);
-    int Rf_countContexts(int, int);
-    SEXP Rf_CreateTag(SEXP);
-    void Rf_defineVar(SEXP, SEXP, SEXP);
-    SEXP Rf_dimgets(SEXP, SEXP);
-    SEXP Rf_dimnamesgets(SEXP, SEXP);
-    SEXP Rf_DropDims(SEXP);
-    */
-
-    /// Compatible way to duplicate an object. Use obj.clone() instead
-    /// for Rust compatibility.
-    pub fn duplicate(&self) -> Self {
-        unsafe { new_owned(Rf_duplicate(self.get())) }
-    }
-
-    /*
-    SEXP Rf_shallow_duplicate(SEXP);
-    SEXP R_duplicate_attr(SEXP);
-    SEXP R_shallow_duplicate_attr(SEXP);
-    SEXP Rf_lazy_duplicate(SEXP);
-    SEXP Rf_duplicated(SEXP, Rboolean);
-    Rboolean R_envHasNoSpecialSymbols(SEXP);
-    SEXP Rf_eval(SEXP, SEXP);
-    SEXP Rf_ExtractSubset(SEXP, SEXP, SEXP);
-    SEXP Rf_findFun(SEXP, SEXP);
-    SEXP Rf_findFun3(SEXP, SEXP, SEXP);
-    void Rf_findFunctionForBody(SEXP);
-    SEXP Rf_findVar(SEXP, SEXP);
-    SEXP Rf_findVarInFrame(SEXP, SEXP);
-    SEXP Rf_findVarInFrame3(SEXP, SEXP, Rboolean);
-    */
-
-    /// Get a specific attribute as a borrowed robj.
-    /// Return R_NilValue on error.
-    pub fn getAttrib(&self, name: &Robj) -> Robj {
-        if self.sexptype() == CHARSXP {
-            // Avoid R error.
-            Robj::from(NULL)
-        } else {
-            unsafe { new_borrowed(Rf_getAttrib(self.get(), name.get())) }
-        }
-    }
-
-    /*
-    SEXP Rf_GetArrayDimnames(SEXP);
-    SEXP Rf_GetColNames(SEXP);
-    void Rf_GetMatrixDimnames(SEXP, SEXP*, SEXP*, const char**, const char**);
-    SEXP Rf_GetOption(SEXP, SEXP);
-    SEXP Rf_GetOption1(SEXP);
-    int Rf_FixupDigits(SEXP, warn_type);
-    int Rf_FixupWidth (SEXP, warn_type);
-    int Rf_GetOptionDigits(void);
-    int Rf_GetOptionWidth(void);
-    SEXP Rf_GetRowNames(SEXP);
-    void Rf_gsetVar(SEXP, SEXP, SEXP);
-    SEXP Rf_install(const char *);
-    SEXP Rf_installChar(SEXP);
-    SEXP Rf_installNoTrChar(SEXP);
-    SEXP Rf_installTrChar(SEXP);
-    SEXP Rf_installDDVAL(int i);
-    SEXP Rf_installS3Signature(const char *, const char *);
-    Rboolean Rf_isFree(SEXP);
-    Rboolean Rf_isOrdered(SEXP);
-    Rboolean Rf_isUnmodifiedSpecSym(SEXP sym, SEXP env);
-    Rboolean Rf_isUnordered(SEXP);
-    Rboolean Rf_isUnsorted(SEXP, Rboolean);
-    SEXP Rf_lengthgets(SEXP, R_len_t);
-    SEXP Rf_xlengthgets(SEXP, R_xlen_t);
-    SEXP R_lsInternal(SEXP, Rboolean);
-    SEXP R_lsInternal3(SEXP, Rboolean, Rboolean);
-    SEXP Rf_match(SEXP, SEXP, int);
-    SEXP Rf_matchE(SEXP, SEXP, int, SEXP);
-    SEXP Rf_namesgets(SEXP, SEXP);
-    SEXP Rf_mkChar(const char *);
-    SEXP Rf_mkCharLen(const char *, int);
-    Rboolean Rf_NonNullStringMatch(SEXP, SEXP);
-    */
-
-    /// Number of columns of a matrix
-    pub fn ncols(&self) -> usize {
-        unsafe { Rf_ncols(self.get()) as usize }
-    }
-
-    /// Number of rows of a matrix
-    pub fn nrows(&self) -> usize {
-        unsafe { Rf_nrows(self.get()) as usize }
-    }
-
-    /*SEXP Rf_nthcdr(SEXP, int);
-    Rboolean Rf_pmatch(SEXP, SEXP, Rboolean);
-    Rboolean Rf_psmatch(const char *, const char *, Rboolean);
-    void Rf_PrintValue(SEXP);
-    void Rf_printwhere(void);
-    void Rf_readS3VarsFromFrame(SEXP, SEXP*, SEXP*, SEXP*, SEXP*, SEXP*, SEXP*);
-    SEXP Rf_setAttrib(SEXP, SEXP, SEXP);
-    void Rf_setSVector(SEXP*, int, SEXP);
-    void Rf_setVar(SEXP, SEXP, SEXP);
-    SEXP Rf_stringSuffix(SEXP, int);
-    SEXPTYPE Rf_str2type(const char *);
-    Rboolean Rf_StringBlank(SEXP);
-    SEXP Rf_substitute(SEXP,SEXP);
-    SEXP Rf_topenv(SEXP, SEXP);
-    const char * Rf_translateChar(SEXP);
-    const char * Rf_translateChar0(SEXP);
-    const char * Rf_translateCharUTF8(SEXP);
-    const char * Rf_type2char(SEXPTYPE);
-    SEXP Rf_type2rstr(SEXPTYPE);
-    SEXP Rf_type2str(SEXPTYPE);
-    SEXP Rf_type2str_nowarn(SEXPTYPE);
-    SEXP R_GetCurrentEnv();
-    Rboolean Rf_isS4(SEXP);
-    SEXP Rf_asS4(SEXP, Rboolean, int);
-    SEXP Rf_S3Class(SEXP);
-    int Rf_isBasicClass(const char *);
-    Rboolean R_cycle_detected(SEXP s, SEXP child);
-    u32 Rf_getCharCE(SEXP);
-    SEXP Rf_mkCharCE(const char *, cetype_t);
-    SEXP Rf_mkCharLenCE(const char *, int, cetype_t);
-    SEXP R_forceAndCall(SEXP e, int n, SEXP rho);
-    */
-
-    /// Internal function used to implement #[extendr] impl
-    pub unsafe fn makeExternalPtr<T>(p: *mut T, tag: Robj, prot: Robj) -> Self {
-        new_owned(R_MakeExternalPtr(
-            p as *mut ::std::os::raw::c_void,
-            tag.get(),
-            prot.get(),
-        ))
-    }
-
-    /// Internal function used to implement #[extendr] impl
-    pub unsafe fn externalPtrAddr<T>(&self) -> *mut T {
-        R_ExternalPtrAddr(self.get()) as *mut T
-    }
-
-    /// Internal function used to implement #[extendr] impl
-    pub unsafe fn externalPtrTag(&self) -> Self {
-        new_borrowed(R_ExternalPtrTag(self.get()))
-    }
-
-    /// Internal function used to implement #[extendr] impl
-    pub unsafe fn externalPtrProtected(&self) -> Self {
-        new_borrowed(R_ExternalPtrProtected(self.get()))
-    }
-
-    pub unsafe fn registerCFinalizer(&self, func: R_CFinalizer_t) {
-        R_RegisterCFinalizer(self.get(), func);
-    }
-
-    // SEXP R_ExternalPtrTag(SEXP s);
-    // SEXP R_ExternalPtrProtected(SEXP s);
-    // void R_ClearExternalPtr(SEXP s);
-    // void R_SetExternalPtrAddr(SEXP s, void *p);
-    // void R_SetExternalPtrTag(SEXP s, SEXP tag);
-    // void R_SetExternalPtrProtected(SEXP s, SEXP p);
-
-    /*
-    SEXP R_MakeWeakRef(SEXP key, SEXP val, SEXP fin, Rboolean onexit);
-    SEXP R_MakeWeakRefC(SEXP key, SEXP val, R_CFinalizer_t fin, Rboolean onexit);
-    SEXP R_WeakRefKey(SEXP w);
-    SEXP R_WeakRefValue(SEXP w);
-    void R_RunWeakRefFinalizer(SEXP w);
-    SEXP R_PromiseExpr(SEXP);
-    SEXP R_ClosureExpr(SEXP);
-    SEXP R_BytecodeExpr(SEXP e);
-    SEXP R_bcEncode(SEXP);
-    SEXP R_bcDecode(SEXP);
-    void R_registerBC(SEXP, SEXP);
-    Rboolean R_checkConstants(Rboolean);
-    Rboolean R_BCVersionOK(SEXP);
-    void R_RestoreHashCount(SEXP rho);
-    Rboolean R_IsPackageEnv(SEXP rho);
-    SEXP R_PackageEnvName(SEXP rho);
-    SEXP R_FindPackageEnv(SEXP info);
-    Rboolean R_IsNamespaceEnv(SEXP rho);
-    SEXP R_NamespaceEnvSpec(SEXP rho);
-    SEXP R_FindNamespace(SEXP info);
-    void R_LockEnvironment(SEXP env, Rboolean bindings);
-    Rboolean R_EnvironmentIsLocked(SEXP env);
-    void R_LockBinding(SEXP sym, SEXP env);
-    void R_unLockBinding(SEXP sym, SEXP env);
-    void R_MakeActiveBinding(SEXP sym, SEXP fun, SEXP env);
-    Rboolean R_BindingIsLocked(SEXP sym, SEXP env);
-    Rboolean R_BindingIsActive(SEXP sym, SEXP env);
-    Rboolean R_HasFancyBindings(SEXP rho);
-    */
-
-    /// Read-only access to attribute list.
-    // fn attrib(&self) -> Robj {
-    // unsafe {new_borrowed(ATTRIB(self.get()))}
-    // }
-
-    /// Copy a vector and resize it.
-    /// See. https://github.com/hadley/r-internals/blob/master/vectors.md
-    pub fn xlengthgets(&self, new_len: usize) -> Result<Robj, AnyError> {
-        unsafe {
-            if self.isVector() {
-                Ok(new_owned(Rf_xlengthgets(self.get(), new_len as R_xlen_t)))
-            } else {
-                Err(AnyError::from("xlengthgets: Not a vector type"))
-            }
-        }
-    }
-
-    /// Allocated an owned object of a certain type.
-    pub fn allocVector(sexptype: u32, len: usize) -> Robj {
-        unsafe { new_owned(Rf_allocVector(sexptype, len as R_xlen_t)) }
-    }
-
-    /// Return true if two arrays have identical dims.
-    pub fn conformable(a: &Robj, b: &Robj) -> bool {
-        unsafe { Rf_conformable(a.get(), b.get()) != 0 }
-    }
-
-    /// Borrow an element from a list.
-    pub fn elt(&self, index: usize) -> Robj {
-        unsafe { Robj::from(Rf_elt(self.get(), index as raw::c_int)) }
-    }
-
-    //Rboolean Rf_inherits(SEXP, const char *);
-
-    /// Return true if this is an array.
-    pub fn isArray(&self) -> bool {
-        unsafe { Rf_isArray(self.get()) != 0 }
-    }
-
-    /// Return true if this is factor.
-    pub fn isFactor(&self) -> bool {
-        unsafe { Rf_isFactor(self.get()) != 0 }
-    }
-
-    /// Return true if this is a data frame.
-    pub fn isFrame(&self) -> bool {
-        unsafe { Rf_isFrame(self.get()) != 0 }
-    }
-
-    /// Return true if this is a function.
-    pub fn isFunction(&self) -> bool {
-        unsafe { Rf_isFunction(self.get()) != 0 }
-    }
-
-    /// Return true if this is an integer vector.
-    pub fn isInteger(&self) -> bool {
-        unsafe { Rf_isInteger(self.get()) != 0 }
-    }
-
-    /// Return true if this is a language object.
-    pub fn isLanguage(&self) -> bool {
-        unsafe { Rf_isLanguage(self.get()) != 0 }
-    }
-
-    /// Return true if this is a vector list.
-    pub fn isList(&self) -> bool {
-        unsafe { Rf_isList(self.get()) != 0 }
-    }
-
-    /// Return true if this is a matrix.
-    pub fn isMatrix(&self) -> bool {
-        unsafe { Rf_isMatrix(self.get()) != 0 }
-    }
-
-    /// Return true if this is a vector list or null.
-    pub fn isNewList(&self) -> bool {
-        unsafe { Rf_isNewList(self.get()) != 0 }
-    }
-
-    /// Return true if this is a numeric vector but not a factor.
-    pub fn isNumber(&self) -> bool {
-        unsafe { Rf_isNumber(self.get()) != 0 }
-    }
-
-    /// Return true if this is a numeric vector but not a factor or complex.
-    pub fn isNumeric(&self) -> bool {
-        unsafe { Rf_isNumeric(self.get()) != 0 }
-    }
-
-    /// Return true if this is a pairlist.
-    pub fn isPairList(&self) -> bool {
-        unsafe { Rf_isPairList(self.get()) != 0 }
-    }
-
-    /// Return true if this is a primitive function.
-    pub fn isPrimitive(&self) -> bool {
-        unsafe { Rf_isPrimitive(self.get()) != 0 }
-    }
-
-    /// Return true if this is a time series vector (see tsp).
-    pub fn isTs(&self) -> bool {
-        unsafe { Rf_isTs(self.get()) != 0 }
-    }
-
-    /// Return true if this is a user defined binop.
-    pub fn isUserBinop(&self) -> bool {
-        unsafe { Rf_isUserBinop(self.get()) != 0 }
-    }
-
-    /// Return true if this is a valid string.
-    pub fn isValidString(&self) -> bool {
-        unsafe { Rf_isValidString(self.get()) != 0 }
-    }
-
-    /// Return true if this is a valid string.
-    pub fn isValidStringF(&self) -> bool {
-        unsafe { Rf_isValidStringF(self.get()) != 0 }
-    }
-
-    /// Return true if this is a vector.
-    pub fn isVector(&self) -> bool {
-        unsafe { Rf_isVector(self.get()) != 0 }
-    }
-
-    /// Return true if this is an atomic vector.
-    pub fn isVectorAtomic(&self) -> bool {
-        unsafe { Rf_isVectorAtomic(self.get()) != 0 }
-    }
-
-    /// Return true if this is a vector list.
-    pub fn isVectorList(&self) -> bool {
-        unsafe { Rf_isVectorList(self.get()) != 0 }
-    }
-
-    /// Return true if this is can be made into a vector.
-    pub fn isVectorizable(&self) -> bool {
-        unsafe { Rf_isVectorizable(self.get()) != 0 }
-    }
-
-    /// Check an external pointer tag
-    /// This may work better by using a symbol cached in a static variable.
-    pub fn check_external_ptr(&self, expected_tag: &str) -> bool {
-        if self.sexptype() == libR_sys::EXTPTRSXP {
-            let tag = unsafe { self.externalPtrTag() };
-            if tag.as_str() == Some(expected_tag) {
-                return true;
-            }
-        }
-        false
-    }
-}
-
-pub unsafe fn new_owned(sexp: SEXP) -> Robj {
-    R_PreserveObject(sexp);
-    Robj::Owned(sexp)
-}
-
-pub unsafe fn new_borrowed(sexp: SEXP) -> Robj {
-    Robj::Borrowed(sexp)
-}
-
-pub unsafe fn new_sys(sexp: SEXP) -> Robj {
-    Robj::Sys(sexp)
-}
-
-/// Compare equality with integer slices.
-impl<'a> PartialEq<[i32]> for Robj {
-    fn eq(&self, rhs: &[i32]) -> bool {
-        self.as_i32_slice() == Some(rhs)
-    }
-}
-
-/// Compare equality with slices of double.
-impl<'a> PartialEq<[f64]> for Robj {
-    fn eq(&self, rhs: &[f64]) -> bool {
-        self.as_f64_slice() == Some(rhs)
-    }
-}
-
-/// Compare equality with strings.
-impl PartialEq<str> for Robj {
-    fn eq(&self, rhs: &str) -> bool {
-        self.as_str() == Some(rhs)
-    }
-}
-
-/// Compare equality with two Robjs.
-impl PartialEq<Robj> for Robj {
-    fn eq(&self, rhs: &Robj) -> bool {
-        if self.sexptype() == rhs.sexptype() && self.len() == rhs.len() {
-            unsafe {
-                let lsexp = self.get();
-                let rsexp = rhs.get();
-                match self.sexptype() {
-                    NILSXP => true,
-                    SYMSXP => PRINTNAME(lsexp) == PRINTNAME(rsexp),
-                    LISTSXP | LANGSXP | DOTSXP => self
-                        .pairlist_iter()
-                        .unwrap()
-                        .eq(rhs.pairlist_iter().unwrap()),
-                    CLOSXP => false,
-                    ENVSXP => false,
-                    PROMSXP => false,
-                    SPECIALSXP => false,
-                    BUILTINSXP => false,
-                    CHARSXP => self.as_str() == rhs.as_str(),
-                    LGLSXP => self.as_logical_slice() == rhs.as_logical_slice(),
-                    INTSXP => self.as_i32_slice() == rhs.as_i32_slice(),
-                    REALSXP => self.as_f64_slice() == rhs.as_f64_slice(),
-                    CPLXSXP => false,
-                    ANYSXP => false,
-                    VECSXP | EXPRSXP => self.list_iter().unwrap().eq(rhs.list_iter().unwrap()),
-                    STRSXP => self.str_iter().unwrap().eq(rhs.str_iter().unwrap()),
-                    BCODESXP => false,
-                    EXTPTRSXP => false,
-                    WEAKREFSXP => false,
-                    RAWSXP => self.as_u8_slice() == rhs.as_u8_slice(),
-                    S4SXP => false,
-                    NEWSXP => false,
-                    FREESXP => false,
-                    _ => false,
-                }
-            }
-        } else {
-            false
-        }
-    }
-}
-
-/// Implement {:?} formatting.
-impl std::fmt::Debug for Robj {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self.sexptype() {
-            NILSXP => write!(f, "NULL"),
-            SYMSXP => write!(f, "Symbol({:?})", self.as_str().unwrap()),
-            // LISTSXP => false,
-            // CLOSXP => false,
-            // ENVSXP => false,
-            // PROMSXP => false,
-            LANGSXP => write!(
-                f,
-                "Lang({:?})",
-                self.pairlist_iter().unwrap().collect::<Vec<Robj>>()
-            ),
-            // SPECIALSXP => false,
-            // BUILTINSXP => false,
-            CHARSXP => write!(f, "Character({:?})", self.as_str().unwrap()),
-            LGLSXP => {
-                let slice = self.as_logical_slice().unwrap();
-                if slice.len() == 1 {
-                    write!(f, "{}", if slice[0].0 == 0 { "FALSE" } else { "TRUE" })
-                } else {
-                    write!(f, "&{:?}", slice)
-                }
-            }
-            INTSXP => {
-                let slice = self.as_i32_slice().unwrap();
-                if slice.len() == 1 {
-                    write!(f, "{:?}", slice[0])
-                } else {
-                    write!(f, "{:?}", self.as_i32_slice().unwrap())
-                }
-            }
-            REALSXP => {
-                let slice = self.as_f64_slice().unwrap();
-                if slice.len() == 1 {
-                    write!(f, "{:?}", slice[0])
-                } else {
-                    write!(f, "{:?}", slice)
-                }
-            }
-            VECSXP => write!(f, "{:?}", self.list_iter().unwrap().collect::<Vec<_>>()),
-            EXPRSXP => write!(
-                f,
-                "Expr({:?})",
-                self.list_iter().unwrap().collect::<Vec<_>>()
-            ),
-            WEAKREFSXP => write!(
-                f,
-                "Weakref({:?})",
-                self.list_iter().unwrap().collect::<Vec<_>>()
-            ),
-            // CPLXSXP => false,
-            STRSXP => {
-                write!(f, "[")?;
-                let mut sep = "";
-                for obj in self.str_iter().unwrap() {
-                    write!(f, "{}{:?}", sep, obj)?;
-                    sep = ", ";
-                }
-                write!(f, "]")
-            }
-            // DOTSXP => false,
-            // ANYSXP => false,
-            // VECSXP => false,
-            // EXPRSXP => false,
-            // BCODESXP => false,
-            // EXTPTRSXP => false,
-            // WEAKREFSXP => false,
-            RAWSXP => {
-                let slice = self.as_u8_slice().unwrap();
-                if slice.len() == 1 {
-                    write!(f, "{}", slice[0])
-                } else {
-                    write!(f, "{:?}", slice)
-                }
-            }
-            // S4SXP => false,
-            // NEWSXP => false,
-            // FREESXP => false,
-            _ => write!(f, "??"),
-        }
-    }
-}
-
-// Internal utf8 to str conversion.
-// Lets not worry about non-ascii/unicode strings for now (or ever).
-unsafe fn to_str<'a>(ptr: *const u8) -> &'a str {
-    let mut len = 0;
-    loop {
-        if *ptr.offset(len) == 0 {
-            break;
-        }
-        len += 1;
-    }
-    let slice = std::slice::from_raw_parts(ptr, len as usize);
-    std::str::from_utf8_unchecked(slice)
-}
-
-/// Borrow an already protected SEXP
-/// Note that the SEXP must outlive the generated object.
-impl From<SEXP> for Robj {
-    fn from(sexp: SEXP) -> Self {
-        unsafe { new_borrowed(sexp) }
-    }
-}
-
-/// Release any owned objects.
-impl Drop for Robj {
-    fn drop(&mut self) {
-        unsafe {
-            match self {
-                Robj::Owned(sexp) => R_ReleaseObject(*sexp),
-                Robj::Borrowed(_) => (),
-                Robj::Sys(_) => (),
-            }
-        }
-    }
-}
-
-/// Convert a null to an Robj.
-impl From<()> for Robj {
-    fn from(_: ()) -> Self {
-        // Note: we do not need to protect this.
-        unsafe { Robj::Sys(R_NilValue) }
-    }
-}
-
-/// Convert a length value to an Robj.
-/// Note: This is good only up to 2^53, but that exceeds the address space
-/// of current generation computers (8PiB)
-impl From<usize> for Robj {
-    fn from(val: usize) -> Self {
-        unsafe {
-            new_owned(if val >= 0x80000000 {
-                Rf_ScalarReal(val as raw::c_double)
-            } else {
-                Rf_ScalarInteger(val as raw::c_int)
-            })
-        }
-    }
-}
-
-/// Convert a wrapped string ref to an Robj char object.
-impl<'a> From<Character<'a>> for Robj {
-    fn from(val: Character) -> Self {
-        unsafe {
-            new_owned(Rf_mkCharLen(
-                val.0.as_ptr() as *const raw::c_char,
-                val.0.len() as i32,
-            ))
-        }
-    }
-}
-
-/// Convert a wrapped string ref to an Robj language object.
-impl<'a> From<Lang<'a>> for Robj {
-    fn from(val: Lang<'a>) -> Self {
-        unsafe {
-            let mut name = Vec::from(val.0.as_bytes());
-            name.push(0);
-            new_owned(Rf_lang1(Rf_install(name.as_ptr() as *const raw::c_char)))
-        }
-    }
-}
-
-/// `ToVectorValue` is a trait that allows many different types
-/// to be converted to vectors. It is used as a type parameter
-/// to `collect_robj()`.
-pub trait ToVectorValue {
-    fn sexptype() -> SEXPTYPE {
-        0
-    }
-
-    fn to_numeric(&self) -> f64 where Self: Sized {
-        0.
-    }
-
-    fn to_integer(&self) -> i32 where Self: Sized  {
-        std::i32::MIN
-    }
-
-    fn to_logical(&self) -> i32 where Self: Sized  {
-        std::i32::MIN
-    }
-
-    fn to_raw(&self) -> u8 where Self: Sized  {
-        0
-    }
-
-    fn to_sexp(&self) -> SEXP where Self: Sized  {
-        unsafe { R_NaString }
-    }
-}
-
-macro_rules! impl_numeric_tvv {
-    ($t: ty) => {
-        impl ToVectorValue for $t {
-            fn sexptype() -> SEXPTYPE {
-                REALSXP
-            }
-        
-            fn to_numeric(&self) -> f64 {
-                *self as f64
-            }
-        }
-        
-        impl ToVectorValue for &$t {
-            fn sexptype() -> SEXPTYPE {
-                REALSXP
-            }
-        
-            fn to_numeric(&self) -> f64 {
-                **self as f64
-            }
-        }
-        
-        impl ToVectorValue for Option<$t> {
-            fn sexptype() -> SEXPTYPE {
-                REALSXP
-            }
-        
-            fn to_numeric(&self) -> f64 {
-                if self.is_some() {
-                    self.unwrap() as f64
-                } else {
-                    unsafe { R_NaReal }
-                }
-            }
-        }
-    }
-}
-
-impl_numeric_tvv!(f64);
-impl_numeric_tvv!(f32);
-
-macro_rules! impl_integer_tvv {
-    ($t: ty) => {
-        impl ToVectorValue for $t {
-            fn sexptype() -> SEXPTYPE {
-                INTSXP
-            }
-
-            fn to_integer(&self) -> i32 {
-                *self as i32
-            }
-        }
-
-        impl ToVectorValue for &$t {
-            fn sexptype() -> SEXPTYPE {
-                INTSXP
-            }
-
-            fn to_integer(&self) -> i32 {
-                **self as i32
-            }
-        }
-
-        impl ToVectorValue for Option<$t> {
-            fn sexptype() -> SEXPTYPE {
-                INTSXP
-            }
-
-            fn to_integer(&self) -> i32 {
-                if self.is_some() {
-                    self.unwrap() as i32
-                } else {
-                    unsafe { R_NaInt }
-                }
-            }
-        }
-    }
-}
-
-impl_integer_tvv!(i8);
-impl_integer_tvv!(i16);
-impl_integer_tvv!(i32);
-impl_integer_tvv!(i64);
-impl_integer_tvv!(u16);
-impl_integer_tvv!(u32);
-impl_integer_tvv!(u64);
-
-impl ToVectorValue for &str {
-    fn sexptype() -> SEXPTYPE {
-        STRSXP
-    }
-
-    fn to_sexp(&self) -> SEXP where Self: Sized  {
-        unsafe { Rf_mkCharLen(self.as_ptr() as *const raw::c_char, self.len() as i32) }
-    }
-}
-
-impl ToVectorValue for &&str {
-    fn sexptype() -> SEXPTYPE {
-        STRSXP
-    }
-
-    fn to_sexp(&self) -> SEXP where Self: Sized  {
-        unsafe { Rf_mkCharLen(self.as_ptr() as *const raw::c_char, self.len() as i32) }
-    }
-}
-
-impl ToVectorValue for Option<&str> {
-    fn sexptype() -> SEXPTYPE {
-        STRSXP
-    }
-
-    fn to_sexp(&self) -> SEXP where Self: Sized {
-        if let Some(s) = self {
-            unsafe { Rf_mkCharLen(s.as_ptr() as *const raw::c_char, s.len() as i32) }
-        } else {
-            unsafe { R_NaString }
-        }
-    }
-}
-
-impl ToVectorValue for String {
-    fn sexptype() -> SEXPTYPE {
-        STRSXP
-    }
-
-    fn to_sexp(&self) -> SEXP where Self: Sized  {
-        unsafe { Rf_mkCharLen(self.as_ptr() as *const raw::c_char, self.len() as i32) }
-    }
-}
-
-impl ToVectorValue for bool {
-    fn sexptype() -> SEXPTYPE {
-        LGLSXP
-    }
-
-    fn to_logical(&self) -> i32 where Self: Sized  {
-        *self as i32
-    }
-}
-
-impl ToVectorValue for &bool {
-    fn sexptype() -> SEXPTYPE {
-        LGLSXP
-    }
-
-    fn to_logical(&self) -> i32 where Self: Sized  {
-        **self as i32
-    }
-}
-
-impl ToVectorValue for Option<bool> {
-    fn sexptype() -> SEXPTYPE {
-        LGLSXP
-    }
-
-    fn to_logical(&self) -> i32 {
-        if self.is_some() {
-            self.unwrap() as i32
-        } else {
-            unsafe { R_NaInt }
-        }
-    }
-}
-
-impl ToVectorValue for u8 {
-    fn sexptype() -> SEXPTYPE {
-        RAWSXP
-    }
-
-    fn to_raw(&self) -> u8 where Self: Sized  {
-        *self
-    }
-}
-
-impl ToVectorValue for &u8 {
-    fn sexptype() -> SEXPTYPE {
-        RAWSXP
-    }
-
-    fn to_raw(&self) -> u8 where Self: Sized  {
-        **self
-    }
-}
-
-// impl ToVectorValue for Option<u8> {
-//     fn sexptype() -> SEXPTYPE {
-//         RAWSXP
-//     }
-
-//     fn to_raw(&self) -> u8 {
-//         if self.is_some() {
-//             self.unwrap()
-//         } else {
-//             0
-//         }
-//     }
-// }
-
-pub trait RobjItertools : Iterator {
-    /// Convert a wide range of iterators to Robj.
-    /// ```
-    /// use extendr_api::*;
-    ///
-    /// extendr_engine::start_r();
-    ///
-    /// // Integer iterators.
-    /// let robj = (0..3).collect_robj();
-    /// assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 1, 2]);
-    ///
-    /// // Logical iterators.
-    /// let robj = (0..3).map(|x| x % 2 == 0).collect_robj();
-    /// assert_eq!(robj.as_logical_vector().unwrap(), vec![true, false, true]);
-    ///
-    /// // Numeric iterators.
-    /// let robj = (0..3).map(|x| x as f64).collect_robj();
-    /// assert_eq!(robj.as_numeric_vector().unwrap(), vec![0., 1., 2.]);
-    ///
-    /// // String iterators.
-    /// let robj = (0..3).map(|x| format!("{}", x)).collect_robj();
-    /// assert_eq!(robj.as_str_vector(), Some(vec!["0", "1", "2"]));
-    /// ```
-    fn collect_robj(self) -> Robj
-        where
-            Self : Iterator,
-            Self : Sized,
-            Self::Item : ToVectorValue
-        {
-        unsafe {
-            if let (len, Some(max)) = self.size_hint().clone() {
-                if len == max {
-                    // Length of the vector is known in advance.
-                    let sexptype = Self::Item::sexptype();
-                    if sexptype != 0 {
-                        let sexp = Rf_allocVector(sexptype, len as R_xlen_t);
-                        R_PreserveObject(sexp);
-                        match sexptype {
-                            REALSXP => {
-                                let ptr = REAL(sexp);
-                                for (i, v) in self.enumerate() {
-                                    *ptr.offset(i as isize) = v.to_numeric();
-                                }
-                            }
-                            INTSXP => {
-                                let ptr = INTEGER(sexp);
-                                for (i, v) in self.enumerate() {
-                                    *ptr.offset(i as isize) = v.to_integer();
-                                }
-                            }
-                            LGLSXP => {
-                                let ptr = LOGICAL(sexp);
-                                for (i, v) in self.enumerate() {
-                                    *ptr.offset(i as isize) = v.to_logical();
-                                }
-                            }
-                            RAWSXP => {
-                                let ptr = RAW(sexp);
-                                for (i, v) in self.enumerate() {
-                                    *ptr.offset(i as isize) = v.to_raw();
-                                }
-                            }
-                            STRSXP => {
-                                for (i, v) in self.enumerate() {
-                                    SET_STRING_ELT(sexp, i as isize, v.to_sexp());
-                                }
-                            }
-                            _ => {
-                                panic!("unexpected SEXPTYPE in collect_robj");
-                            }
-                        }
-                        return Robj::Owned(sexp);
-                    } else {
-                        return Robj::from(());
-                    }
-                }
-            }
-
-            // If the size is indeterminate, create a vector and call recursively.
-            let vec : Vec<_> = self.collect();
-            assert!(vec.iter().size_hint() == (vec.len(), Some(vec.len())));
-            vec.into_iter().collect_robj()
-        }
-    }
-}
-
-// Thanks to *pretzelhammer* on stackoverflow for this.
-impl<T> RobjItertools for T where T: Iterator {}
-
-impl<T> From<T> for Robj
-where
-    T : ToVectorValue
-{
-    fn from(scalar: T) -> Self {
-        Some(scalar).into_iter().collect_robj()
-    }
-}
-
-impl<T> From<&[T]> for Robj
-where
-    T : ToVectorValue + Clone
-{
-    fn from(slice: &[T]) -> Self {
-        slice.into_iter().cloned().collect_robj()
-    }
-}
-
-impl<T> From<Vec<T>> for Robj
-where
-    T : ToVectorValue + Clone
-{
-    fn from(slice: Vec<T>) -> Self {
-        slice.into_iter().collect_robj()
-    }
-}
-
-// Iterator over the objects in a vector or string.
-#[derive(Clone)]
-pub struct VecIter {
-    vector: SEXP,
-    i: usize,
-    len: usize,
-}
-
-impl Iterator for VecIter {
-    type Item = Robj;
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (self.len, Some(self.len))
-    }
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let i = self.i;
-        self.i += 1;
-        if i >= self.len {
-            return None;
-        } else {
-            Some(Robj::from(unsafe { VECTOR_ELT(self.vector, i as isize) }))
-        }
-    }
-
-    fn nth(&mut self, n: usize) -> Option<Self::Item> {
-        self.i += n;
-        self.next()
-    }
-}
-
-impl std::fmt::Debug for VecIter {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "[")?;
-        for s in self.clone() {
-            write!(f, "{:?}", s)?;
-        }
-        write!(f, "]")
-    }
-}
-
-/// Iterator over the objects in a vector or string.
-pub type IntegerIter<'a> = std::slice::Iter<'a, i32>;
-pub type NumericIter<'a> = std::slice::Iter<'a, f64>;
-pub type LogicalIter<'a> = std::slice::Iter<'a, Bool>;
-
-// Iterator over the objects in a vector or string.
-#[derive(Clone)]
-pub struct ListIter {
-    list_elem: SEXP,
-}
-
-impl ListIter {
-    /// Make an empty list iterator.
-    pub fn new() -> Self {
-        unsafe { Self { list_elem: R_NilValue } }
-    }
-}
-
-impl Iterator for ListIter {
-    type Item = Robj;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        unsafe {
-            let sexp = self.list_elem;
-            if sexp == R_NilValue {
-                None
-            } else {
-                self.list_elem = CDR(sexp);
-                Some(new_borrowed(CAR(sexp)))
-            }
-        }
-    }
-}
-
-impl std::fmt::Debug for ListIter {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "[")?;
-        for s in self.clone() {
-            write!(f, "{:?}", s)?;
-        }
-        write!(f, "]")
-    }
-}
-#[derive(Clone)]
-pub struct StrIter {
-    vector: SEXP,
-    i: usize,
-    len: usize,
-    levels: SEXP,
-}
-
-impl StrIter {
-    /// Make an empty str iterator.
-    pub fn new() -> Self {
-        unsafe { Self { vector: R_NilValue, i: 0, len: 0, levels: R_NilValue } }
-    }
-}
-
-// Get a string reference from a CHARSXP
-fn str_from_strsxp<'a>(sexp: SEXP, index: isize) -> &'a str {
-    unsafe {
-        if index < 0 || index >= Rf_xlength(sexp) {
-            ""
-        } else {
-            let charsxp = STRING_ELT(sexp, index);
-            let ptr = R_CHAR(charsxp) as *const u8;
-            let slice = std::slice::from_raw_parts(ptr, Rf_xlength(charsxp) as usize);
-            std::str::from_utf8_unchecked(slice)
-        }
-    }
-}
-
-impl Iterator for StrIter {
-    type Item = &'static str;
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (self.len, Some(self.len))
-    }
-
-    fn next(&mut self) -> Option<Self::Item> {
-        unsafe {
-            let i = self.i;
-            self.i += 1;
-            if i >= self.len {
-                return None;
-            } else if TYPEOF(self.vector) as u32 == STRSXP {
-                Some(str_from_strsxp(self.vector, i as isize))
-            } else if TYPEOF(self.vector) as u32 == INTSXP && TYPEOF(self.levels) as u32 == STRSXP {
-                let j = *(INTEGER(self.vector).offset(i as isize));
-                Some(str_from_strsxp(self.levels, j as isize - 1))
-            } else {
-                return None;
-            }
-        }
-    }
-
-    fn nth(&mut self, n: usize) -> Option<Self::Item> {
-        self.i += n;
-        self.next()
-    }
-}
-
-impl std::fmt::Debug for StrIter {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "[")?;
-        let mut comma = "";
-        for s in self.clone() {
-            write!(f, "{}{:?}", comma, s)?;
-            comma = ", ";
-        }
-        write!(f, "]")
-    }
-}
-
-type NamedListIter = std::iter::Zip<StrIter, VecIter>;
-
-#[cfg(test)]
-mod tests {
-    use extendr_engine::*;
-    use super::*;
-
-    #[test]
-    fn test_debug() {
-        // Special values
-        assert_eq!(format!("{:?}", Robj::from(NULL)), "NULL");
-        assert_eq!(format!("{:?}", Robj::from(TRUE)), "TRUE");
-        assert_eq!(format!("{:?}", Robj::from(FALSE)), "FALSE");
-
-        // Scalars
-        assert_eq!(format!("{:?}", Robj::from(1)), "1");
-        assert_eq!(format!("{:?}", Robj::from(1.)), "1.0");
-        assert_eq!(format!("{:?}", Robj::from("hello")), "[\"hello\"]");
-        let s = "hello".to_string();
-        assert_eq!(format!("{:?}", Robj::from(s)), "[\"hello\"]");
-
-        // Vectors
-        assert_eq!(format!("{:?}", Robj::from(&[1, 2, 3][..])), "[1, 2, 3]");
-        assert_eq!(
-            format!("{:?}", Robj::from(&[1., 2., 3.][..])),
-            "[1.0, 2.0, 3.0]"
-        );
-        assert_eq!(
-            format!("{:?}", Robj::from(&[1_u8, 2_u8, 3_u8][..])),
-            "[1, 2, 3]"
-        );
-
-        // Wrappers
-        assert_eq!(format!("{:?}", Robj::from(Symbol("x"))), "Symbol(\"x\")");
-        assert_eq!(
-            format!("{:?}", Robj::from(Character("x"))),
-            "Character(\"x\")"
-        );
-        assert_eq!(
-            format!("{:?}", Robj::from(Lang("x"))),
-            "Lang([Symbol(\"x\")])"
-        );
-
-        // Logical
-        assert_eq!(
-            format!("{:?}", Robj::from(&[Bool(1), Bool(0)][..])),
-            "&[Bool(1), Bool(0)]"
-        );
-    }
-
-    #[test]
-    fn test_from_robj() {
-        assert_eq!(<bool>::from_robj(&Robj::from(true)), Ok(true));
-        assert_eq!(<u8>::from_robj(&Robj::from(1)), Ok(1));
-        assert_eq!(<u16>::from_robj(&Robj::from(1)), Ok(1));
-        assert_eq!(<u32>::from_robj(&Robj::from(1)), Ok(1));
-        assert_eq!(<u64>::from_robj(&Robj::from(1)), Ok(1));
-        assert_eq!(<i8>::from_robj(&Robj::from(1)), Ok(1));
-        assert_eq!(<i16>::from_robj(&Robj::from(1)), Ok(1));
-        assert_eq!(<i32>::from_robj(&Robj::from(1)), Ok(1));
-        assert_eq!(<i64>::from_robj(&Robj::from(1)), Ok(1));
-        assert_eq!(<f32>::from_robj(&Robj::from(1)), Ok(1.));
-        assert_eq!(<f64>::from_robj(&Robj::from(1)), Ok(1.));
-
-        assert_eq!(<Vec::<i32>>::from_robj(&Robj::from(1)), Ok(vec![1]));
-        assert_eq!(<Vec::<f64>>::from_robj(&Robj::from(1.)), Ok(vec![1.]));
-
-        let hello = Robj::from("hello");
-        assert_eq!(<&str>::from_robj(&hello), Ok("hello"));
-
-        // conversion from a vector to a scalar value
-        assert_eq!(<i32>::from_robj(&Robj::from(vec![].as_slice() as &[i32])), Err("Input must be of length 1. Vector of length zero given."));
-        assert_eq!(<i32>::from_robj(&Robj::from(vec![1].as_slice() as &[i32])), Ok(1));
-        assert_eq!(<i32>::from_robj(&Robj::from(vec![1, 2].as_slice() as &[i32])), Err("Input must be of length 1. Vector of length >1 given."));
-
-        use std::collections::HashMap;
-        let list = Robj::eval_string("list(a=1L, b=2L)").unwrap();
-        let hmap1 = [("a".into(), 1.into()), ("b".into(), 2.into())].iter().cloned().collect::<HashMap<String, Robj>>();
-        let hmap2 = [("a", 1.into()), ("b", 2.into())].iter().cloned().collect::<HashMap<&str, Robj>>();
-        let hmap_owned = <HashMap<String, Robj>>::from_robj(&list).unwrap();
-        let hmap_borrowed = <HashMap<&str, Robj>>::from_robj(&list).unwrap();
-        assert_eq!(hmap_owned, hmap1);
-        assert_eq!(hmap_borrowed, hmap2);
-
-        assert!(hmap_owned["a"].is_owned());
-        assert_eq!(hmap_owned["a"], Robj::from(1));
-        assert_eq!(hmap_owned["b"], Robj::from(2));
-
-        assert_eq!(hmap_borrowed["a"], Robj::from(1));
-        assert_eq!(hmap_borrowed["b"], Robj::from(2));
-
-        let na_integer = Robj::eval_string("NA_integer_").unwrap();
-        assert!(<i32>::from_robj(&na_integer).is_err());
-        assert_eq!(<Option<i32>>::from_robj(&na_integer), Ok(None));
-        assert_eq!(<Option<i32>>::from_robj(&Robj::from(1)), Ok(Some(1)));
-        assert!(<Option<i32>>::from_robj(&Robj::from(&[1, 2][..])).is_err());
-
-        let na_bool = Robj::eval_string("TRUE == NA").unwrap();
-        assert!(<bool>::from_robj(&na_bool).is_err());
-        assert_eq!(<Option<bool>>::from_robj(&na_bool), Ok(None));
-        assert_eq!(<Option<bool>>::from_robj(&Robj::from(true)), Ok(Some(true)));
-        assert!(<Option<bool>>::from_robj(&Robj::from(&[true, false][..])).is_err());
-
-        let na_real = Robj::eval_string("NA_real_").unwrap();
-        assert!(<f64>::from_robj(&na_real).is_err());
-        assert_eq!(<Option<f64>>::from_robj(&na_real), Ok(None));
-        assert_eq!(<Option<f64>>::from_robj(&Robj::from(1.)), Ok(Some(1.)));
-        assert!(<Option<f64>>::from_robj(&Robj::from(&[1., 2.][..])).is_err());
-
-        let na_string = Robj::eval_string("NA_character_").unwrap();
-        assert!(<&str>::from_robj(&na_string).is_err());
-        assert_eq!(<Option<&str>>::from_robj(&na_string), Ok(None));
-        assert_eq!(<Option<&str>>::from_robj(&Robj::from("1")), Ok(Some("1")));
-        assert!(<Option<&str>>::from_robj(&Robj::from(&["1", "2"][..])).is_err());
-
-        let na_string = Robj::eval_string("NA_character_").unwrap();
-        assert!(<String>::from_robj(&na_string).is_err());
-        assert_eq!(<Option<String>>::from_robj(&na_string), Ok(None));
-        assert_eq!(<Option<String>>::from_robj(&Robj::from("1")), Ok(Some("1".to_string())));
-        assert!(<Option<String>>::from_robj(&Robj::from(&["1", "2"][..])).is_err());
-    }
-    #[test]
-    fn test_to_robj() {
-        assert_eq!(Robj::from(true), Robj::from(&[Bool::from(true)][..]));
-        //assert_eq!(Robj::from(1_u8), Robj::from(1));
-        assert_eq!(Robj::from(1_u16), Robj::from(1));
-        assert_eq!(Robj::from(1_u32), Robj::from(1));
-        assert_eq!(Robj::from(1_u64), Robj::from(1));
-        assert_eq!(Robj::from(1_i8), Robj::from(1));
-        assert_eq!(Robj::from(1_i16), Robj::from(1));
-        assert_eq!(Robj::from(1_i32), Robj::from(1));
-        assert_eq!(Robj::from(1_i64), Robj::from(1));
-        assert_eq!(Robj::from(1.0_f32), Robj::from(1.));
-        assert_eq!(Robj::from(1.0_f64), Robj::from(1.));
-
-        let ab = Robj::from(vec!["a", "b"]);
-        let ab2 = Robj::from(vec!["a".to_string(), "b".to_string()]);
-        assert_eq!(ab, ab2);
-        assert_eq!(format!("{:?}", ab), "[\"a\", \"b\"]");
-        assert_eq!(format!("{:?}", ab2), "[\"a\", \"b\"]");
-
-        assert_eq!(Robj::from(Some(1)), Robj::from(1));
-        assert!(!Robj::from(Some(1)).is_na());
-        assert!(Robj::from(<Option<i32>>::None).is_na());
-
-        assert_eq!(Robj::from(Some(true)), Robj::from(true));
-        assert!(!Robj::from(Some(true)).is_na());
-        assert!(Robj::from(<Option<bool>>::None).is_na());
-
-        assert_eq!(Robj::from(Some(1.)), Robj::from(1.));
-        assert!(!Robj::from(Some(1.)).is_na());
-        assert!(Robj::from(<Option<f64>>::None).is_na());
-
-        assert_eq!(Robj::from(Some("xyz")), Robj::from("xyz"));
-        assert!(!Robj::from(Some("xyz")).is_na());
-        assert!(Robj::from(<Option<&str>>::None).is_na());
-    }
-
-    #[test]
-    fn parse_test() -> Result<(), AnyError> {
-        start_r();
-        let p = Robj::parse("print(1L);print(1L);")?;
-        assert_eq!(
-            format!("{:?}", p),
-            "Expr([Lang([Symbol(\"print\"), 1]), Lang([Symbol(\"print\"), 1])])"
-        );
-
-        let p = Robj::eval_string("1L + 1L")?;
-        assert_eq!(p, Robj::from(2));
-        Ok(())
-    }
-
-    #[test]
-    fn output_iterator_test() -> Result<(), AnyError> {
-        start_r();
-
-        // Allocation where size is known in advance.
-        let robj = (0..3).collect_robj();
-        assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 1, 2]);
-
-        let robj = [0, 1, 2].iter().collect_robj();
-        assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 1, 2]);
-
-        let robj = (0..3).map(|x| x % 2 == 0).collect_robj();
-        assert_eq!(robj.as_logical_vector().unwrap(), vec![true, false, true]);
-
-        let robj = [true, false, true].iter().collect_robj();
-        assert_eq!(robj.as_logical_vector().unwrap(), vec![true, false, true]);
-
-        let robj = (0..3).map(|x| x as f64).collect_robj();
-        assert_eq!(robj.as_numeric_vector().unwrap(), vec![0., 1., 2.]);
-
-        let robj = [0., 1., 2.].iter().collect_robj();
-        assert_eq!(robj.as_numeric_vector().unwrap(), vec![0., 1., 2.]);
-
-        let robj = (0..3).map(|x| format!("{}", x)).collect_robj();
-        assert_eq!(robj.as_str_vector(), Some(vec!["0", "1", "2"]));
-
-        let robj = ["0", "1", "2"].iter().collect_robj();
-        assert_eq!(robj.as_str_vector(), Some(vec!["0", "1", "2"]));
-
-        // Fallback allocation where size is not known in advance.
-        let robj = (0..3).filter(|&x| x != 1).collect_robj();
-        assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 2]);
-
-        let robj = (0..3).filter(|&x| x != 1).map(|x| x as f64).collect_robj();
-        assert_eq!(robj.as_numeric_vector().unwrap(), vec![0., 2.]);
-
-        let robj = (0..3).filter(|&x| x != 1).map(|x| format!("{}", x)).collect_robj();
-        assert_eq!(robj.as_str_vector(), Some(vec!["0", "2"]));
-
-        Ok(())
-    }
-    
-    // Test that we can use Iterators as the input to functions.
-    // eg.
-    // #[extendr]
-    // fn fred(a: NumericIter, b: NumericIter) -> NumericIter {
-    // }
-    #[test]
-    fn input_iterator_test() {
-        start_r();
-
-        let src : &[&str] = &["1", "2", "3"];
-        let robj = Robj::from(src);
-        let iter = <StrIter>::from_robj(&robj).unwrap();
-        assert_eq!(iter.collect::<Vec<_>>(), src);
-
-        let src = &[Robj::from(1), Robj::from(2), Robj::from(3)];
-        let robj = Robj::from(List(src));
-        let iter = <VecIter>::from_robj(&robj).unwrap();
-        assert_eq!(iter.collect::<Vec<_>>(), src);
-
-        let src: &[i32] = &[1, 2, 3];
-        let robj = Robj::from(src);
-        let iter = <IntegerIter>::from_robj(&robj).unwrap();
-        assert_eq!(iter.cloned().collect::<Vec<_>>(), src);
-
-        let src: &[f64] = &[1., 2., 3.];
-        let robj = Robj::from(src);
-        let iter = <NumericIter>::from_robj(&robj).unwrap();
-        assert_eq!(iter.cloned().collect::<Vec<_>>(), src);
-
-        let src: &[Bool] = &[Bool::from(true), Bool::from(false), Bool::from(true)];
-        let robj = Robj::from(src);
-        let iter = <LogicalIter>::from_robj(&robj).unwrap();
-        assert_eq!(iter.cloned().collect::<Vec<_>>(), src);
-    }
-}
diff --git a/extendr-api/src/robj/from_robj.rs b/extendr-api/src/robj/from_robj.rs
new file mode 100644
index 000000000..7a5e0f615
--- /dev/null
+++ b/extendr-api/src/robj/from_robj.rs
@@ -0,0 +1,236 @@
+use super::*;
+
+/// Trait used for incomming parameter conversion.
+pub trait FromRobj<'a>: Sized {
+    // Convert an incomming Robj from R into a value or an error.
+    fn from_robj(_robj: &'a Robj) -> Result<Self, &'static str> {
+        Err("unable to convert value from R object")
+    }
+}
+
+macro_rules! impl_prim_from_robj {
+    ($t: ty) => {
+        impl<'a> FromRobj<'a> for $t {
+            fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+                if let Some(v) = robj.as_integer_slice() {
+                    match v.len() {
+                        0 => Err("Input must be of length 1. Vector of length zero given."),
+                        1 => {
+                            if !v[0].is_na() {
+                                Ok(v[0] as Self)
+                            } else {
+                                Err("Input must not be NA.")
+                            }
+                        }
+                        _ => Err("Input must be of length 1. Vector of length >1 given."),
+                    }
+                } else if let Some(v) = robj.as_real_slice() {
+                    match v.len() {
+                        0 => Err("Input must be of length 1. Vector of length zero given."),
+                        1 => {
+                            if !v[0].is_na() {
+                                Ok(v[0] as Self)
+                            } else {
+                                Err("Input must not be NA.")
+                            }
+                        }
+                        _ => Err("Input must be of length 1. Vector of length >1 given."),
+                    }
+                } else {
+                    Err("unable to convert R object to primitive")
+                }
+            }
+        }
+    };
+}
+
+impl_prim_from_robj!(u8);
+impl_prim_from_robj!(u16);
+impl_prim_from_robj!(u32);
+impl_prim_from_robj!(u64);
+impl_prim_from_robj!(i8);
+impl_prim_from_robj!(i16);
+impl_prim_from_robj!(i32);
+impl_prim_from_robj!(i64);
+impl_prim_from_robj!(f32);
+impl_prim_from_robj!(f64);
+
+impl<'a> FromRobj<'a> for bool {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if let Some(v) = robj.as_logical_slice() {
+            match v.len() {
+                0 => Err("Input must be of length 1. Vector of length zero given."),
+                1 => {
+                    if !v[0].is_na() {
+                        Ok(v[0].to_bool())
+                    } else {
+                        Err("Input must not be NA.")
+                    }
+                }
+                _ => Err("Input must be of length 1. Vector of length >1 given."),
+            }
+        } else {
+            Err("Not a logical object.")
+        }
+    }
+}
+
+impl<'a> FromRobj<'a> for &'a str {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if robj.is_na() {
+            Err("Input must not be NA.")
+        } else if let Some(s) = robj.as_str() {
+            Ok(s)
+        } else {
+            Err("Not a string object.")
+        }
+    }
+}
+
+impl<'a> FromRobj<'a> for String {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if robj.is_na() {
+            Err("Input must not be NA.")
+        } else if let Some(s) = robj.as_str() {
+            Ok(s.to_string())
+        } else {
+            Err("not a string object")
+        }
+    }
+}
+
+impl<'a> FromRobj<'a> for Vec<i32> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if let Some(v) = robj.as_integer_slice() {
+            Ok(Vec::from(v))
+        } else {
+            Err("not an integer or logical vector")
+        }
+    }
+}
+
+impl<'a> FromRobj<'a> for Vec<f64> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if let Some(v) = robj.as_real_slice() {
+            Ok(Vec::from(v))
+        } else {
+            Err("not a floating point vector")
+        }
+    }
+}
+
+macro_rules! impl_iter_from_robj {
+    ($t: ty, $iter_fn: ident, $msg: expr) => {
+        impl<'a> FromRobj<'a> for $t {
+            fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+                if let Some(v) = robj.$iter_fn() {
+                    Ok(v)
+                } else {
+                    Err($msg)
+                }
+            }
+        }
+    };
+}
+
+impl_iter_from_robj!(StrIter, str_iter, "Not a character vector.");
+impl_iter_from_robj!(VecIter, list_iter, "Not a list.");
+impl_iter_from_robj!(IntegerIter<'a>, integer_iter, "Not an integer vector.");
+impl_iter_from_robj!(RealIter<'a>, real_iter, "Not a real vector.");
+impl_iter_from_robj!(LogicalIter<'a>, logical_iter, "Not a logical vector.");
+
+/// Pass-through Robj conversion.
+impl<'a> FromRobj<'a> for Robj {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        Ok(unsafe { new_borrowed(robj.get()) })
+    }
+}
+
+impl<'a> FromRobj<'a> for HashMap<String, Robj> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if let Some(iter) = robj.named_list_iter() {
+            Ok(iter
+                .map(|(k, v)| (k.to_string(), v.to_owned()))
+                .collect::<HashMap<String, Robj>>())
+        } else {
+            Err("expected a list")
+        }
+    }
+}
+
+impl<'a> FromRobj<'a> for HashMap<&str, Robj> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if let Some(iter) = robj.named_list_iter() {
+            Ok(iter.map(|(k, v)| (k, v)).collect::<HashMap<&str, Robj>>())
+        } else {
+            Err("expected a list")
+        }
+    }
+}
+
+// NA-sensitive integer input handling
+impl<'a> FromRobj<'a> for Option<i32> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if robj.is_na() {
+            Ok(None)
+        } else if let Some(val) = robj.as_integer() {
+            Ok(Some(val))
+        } else {
+            Err("expected an integer scalar")
+        }
+    }
+}
+
+// NA-sensitive logical input handling
+impl<'a> FromRobj<'a> for Option<bool> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if let Some(val) = robj.as_logical() {
+            if val.is_na() {
+                Ok(None)
+            } else {
+                Ok(Some(val.is_true()))
+            }
+        } else {
+            Err("expected a logical scalar")
+        }
+    }
+}
+
+// NA-sensitive real input handling
+impl<'a> FromRobj<'a> for Option<f64> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if robj.is_na() {
+            Ok(None)
+        } else if let Some(val) = robj.as_real() {
+            Ok(Some(val))
+        } else {
+            Err("expected a real scalar")
+        }
+    }
+}
+
+// NA-sensitive string input handling
+impl<'a> FromRobj<'a> for Option<&'a str> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if robj.is_na() {
+            Ok(None)
+        } else if let Some(val) = robj.as_str() {
+            Ok(Some(val))
+        } else {
+            Err("expected a character scalar")
+        }
+    }
+}
+
+// NA-sensitive string input handling
+impl<'a> FromRobj<'a> for Option<String> {
+    fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
+        if robj.is_na() {
+            Ok(None)
+        } else if let Some(val) = robj.as_str() {
+            Ok(Some(val.to_string()))
+        } else {
+            Err("expected a character scalar")
+        }
+    }
+}
diff --git a/extendr-api/src/robj/into_robj.rs b/extendr-api/src/robj/into_robj.rs
new file mode 100644
index 000000000..efb104e3e
--- /dev/null
+++ b/extendr-api/src/robj/into_robj.rs
@@ -0,0 +1,500 @@
+use super::*;
+
+/// Borrow an already protected SEXP
+/// Note that the SEXP must outlive the generated object.
+impl From<SEXP> for Robj {
+    fn from(sexp: SEXP) -> Self {
+        unsafe { new_borrowed(sexp) }
+    }
+}
+
+/// Convert a null to an Robj.
+impl From<()> for Robj {
+    fn from(_: ()) -> Self {
+        // Note: we do not need to protect this.
+        unsafe { Robj::Sys(R_NilValue) }
+    }
+}
+
+/// Convert a wrapped string ref to an Robj char object.
+impl<'a> From<Character<'a>> for Robj {
+    fn from(val: Character) -> Self {
+        unsafe {
+            new_owned(Rf_mkCharLen(
+                val.0.as_ptr() as *const raw::c_char,
+                val.0.len() as i32,
+            ))
+        }
+    }
+}
+
+/// Convert an Robj reference into a borrowed Robj.
+impl From<&Robj> for Robj {
+    // Note: we should probably have a much better reference
+    // mechanism as double-free or underprotection is a distinct possibility.
+    fn from(val: &Robj) -> Self {
+        unsafe { new_borrowed(val.get()) }
+    }
+}
+
+/// `ToVectorValue` is a trait that allows many different types
+/// to be converted to vectors. It is used as a type parameter
+/// to `collect_robj()`.
+pub trait ToVectorValue {
+    fn sexptype() -> SEXPTYPE {
+        0
+    }
+
+    fn to_real(&self) -> f64
+    where
+        Self: Sized,
+    {
+        0.
+    }
+
+    fn to_integer(&self) -> i32
+    where
+        Self: Sized,
+    {
+        std::i32::MIN
+    }
+
+    fn to_logical(&self) -> i32
+    where
+        Self: Sized,
+    {
+        std::i32::MIN
+    }
+
+    fn to_raw(&self) -> u8
+    where
+        Self: Sized,
+    {
+        0
+    }
+
+    fn to_sexp(&self) -> SEXP
+    where
+        Self: Sized,
+    {
+        unsafe { R_NaString }
+    }
+}
+
+macro_rules! impl_real_tvv {
+    ($t: ty) => {
+        impl ToVectorValue for $t {
+            fn sexptype() -> SEXPTYPE {
+                REALSXP
+            }
+
+            fn to_real(&self) -> f64 {
+                *self as f64
+            }
+        }
+
+        impl ToVectorValue for &$t {
+            fn sexptype() -> SEXPTYPE {
+                REALSXP
+            }
+
+            fn to_real(&self) -> f64 {
+                **self as f64
+            }
+        }
+
+        impl ToVectorValue for Option<$t> {
+            fn sexptype() -> SEXPTYPE {
+                REALSXP
+            }
+
+            fn to_real(&self) -> f64 {
+                if self.is_some() {
+                    self.unwrap() as f64
+                } else {
+                    unsafe { R_NaReal }
+                }
+            }
+        }
+    };
+}
+
+impl_real_tvv!(f64);
+impl_real_tvv!(f32);
+impl_real_tvv!(usize);
+
+macro_rules! impl_integer_tvv {
+    ($t: ty) => {
+        impl ToVectorValue for $t {
+            fn sexptype() -> SEXPTYPE {
+                INTSXP
+            }
+
+            fn to_integer(&self) -> i32 {
+                *self as i32
+            }
+        }
+
+        impl ToVectorValue for &$t {
+            fn sexptype() -> SEXPTYPE {
+                INTSXP
+            }
+
+            fn to_integer(&self) -> i32 {
+                **self as i32
+            }
+        }
+
+        impl ToVectorValue for Option<$t> {
+            fn sexptype() -> SEXPTYPE {
+                INTSXP
+            }
+
+            fn to_integer(&self) -> i32 {
+                if self.is_some() {
+                    self.unwrap() as i32
+                } else {
+                    unsafe { R_NaInt }
+                }
+            }
+        }
+    };
+}
+
+impl_integer_tvv!(i8);
+impl_integer_tvv!(i16);
+impl_integer_tvv!(i32);
+impl_integer_tvv!(i64);
+impl_integer_tvv!(u16);
+impl_integer_tvv!(u32);
+impl_integer_tvv!(u64);
+
+macro_rules! impl_str_tvv {
+    ($t: ty) => {
+        impl ToVectorValue for $t {
+            fn sexptype() -> SEXPTYPE {
+                STRSXP
+            }
+
+            fn to_sexp(&self) -> SEXP
+            where
+                Self: Sized,
+            {
+                let s: &str = self.as_ref();
+                unsafe { Rf_mkCharLen(s.as_ptr() as *const raw::c_char, s.len() as i32) }
+            }
+        }
+
+        impl ToVectorValue for &$t {
+            fn sexptype() -> SEXPTYPE {
+                STRSXP
+            }
+
+            fn to_sexp(&self) -> SEXP
+            where
+                Self: Sized,
+            {
+                let s: &str = self.as_ref();
+                unsafe { Rf_mkCharLen(s.as_ptr() as *const raw::c_char, s.len() as i32) }
+            }
+        }
+
+        impl ToVectorValue for Option<$t> {
+            fn sexptype() -> SEXPTYPE {
+                STRSXP
+            }
+
+            fn to_sexp(&self) -> SEXP
+            where
+                Self: Sized,
+            {
+                if let Some(s) = self {
+                    let s: &str = s.as_ref();
+                    unsafe { Rf_mkCharLen(s.as_ptr() as *const raw::c_char, s.len() as i32) }
+                } else {
+                    unsafe { R_NaString }
+                }
+            }
+        }
+    };
+}
+
+impl_str_tvv! {&str}
+impl_str_tvv! {String}
+
+impl ToVectorValue for bool {
+    fn sexptype() -> SEXPTYPE {
+        LGLSXP
+    }
+
+    fn to_logical(&self) -> i32
+    where
+        Self: Sized,
+    {
+        *self as i32
+    }
+}
+
+impl ToVectorValue for &bool {
+    fn sexptype() -> SEXPTYPE {
+        LGLSXP
+    }
+
+    fn to_logical(&self) -> i32
+    where
+        Self: Sized,
+    {
+        **self as i32
+    }
+}
+
+impl ToVectorValue for Bool {
+    fn sexptype() -> SEXPTYPE {
+        LGLSXP
+    }
+
+    fn to_logical(&self) -> i32
+    where
+        Self: Sized,
+    {
+        self.0
+    }
+}
+
+impl ToVectorValue for &Bool {
+    fn sexptype() -> SEXPTYPE {
+        LGLSXP
+    }
+
+    fn to_logical(&self) -> i32
+    where
+        Self: Sized,
+    {
+        self.0
+    }
+}
+
+impl ToVectorValue for Option<bool> {
+    fn sexptype() -> SEXPTYPE {
+        LGLSXP
+    }
+
+    fn to_logical(&self) -> i32 {
+        if self.is_some() {
+            self.unwrap() as i32
+        } else {
+            unsafe { R_NaInt }
+        }
+    }
+}
+
+impl ToVectorValue for u8 {
+    fn sexptype() -> SEXPTYPE {
+        RAWSXP
+    }
+
+    fn to_raw(&self) -> u8
+    where
+        Self: Sized,
+    {
+        *self
+    }
+}
+
+impl ToVectorValue for &u8 {
+    fn sexptype() -> SEXPTYPE {
+        RAWSXP
+    }
+
+    fn to_raw(&self) -> u8
+    where
+        Self: Sized,
+    {
+        **self
+    }
+}
+
+/// Extensions to iterators for R objects including [RobjItertools::collect_robj()].
+pub trait RobjItertools: Iterator {
+    /// Convert a wide range of iterators to Robj.
+    /// ```
+    /// use extendr_api::*;
+    ///
+    /// extendr_engine::start_r();
+    ///
+    /// // Integer iterators.
+    /// let robj = (0..3).collect_robj();
+    /// assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 1, 2]);
+    ///
+    /// // Logical iterators.
+    /// let robj = (0..3).map(|x| x % 2 == 0).collect_robj();
+    /// assert_eq!(robj.as_logical_vector().unwrap(), vec![TRUE, FALSE, TRUE]);
+    ///
+    /// // Numeric iterators.
+    /// let robj = (0..3).map(|x| x as f64).collect_robj();
+    /// assert_eq!(robj.as_real_vector().unwrap(), vec![0., 1., 2.]);
+    ///
+    /// // String iterators.
+    /// let robj = (0..3).map(|x| format!("{}", x)).collect_robj();
+    /// assert_eq!(robj.as_str_vector(), Some(vec!["0", "1", "2"]));
+    /// ```
+    fn collect_robj(self) -> Robj
+    where
+        Self: Iterator,
+        Self: Sized,
+        Self::Item: ToVectorValue,
+    {
+        unsafe {
+            if let (len, Some(max)) = self.size_hint().clone() {
+                if len == max {
+                    // Length of the vector is known in advance.
+                    let sexptype = Self::Item::sexptype();
+                    if sexptype != 0 {
+                        let sexp = Rf_allocVector(sexptype, len as R_xlen_t);
+                        R_PreserveObject(sexp);
+                        match sexptype {
+                            REALSXP => {
+                                let ptr = REAL(sexp);
+                                for (i, v) in self.enumerate() {
+                                    *ptr.offset(i as isize) = v.to_real();
+                                }
+                            }
+                            INTSXP => {
+                                let ptr = INTEGER(sexp);
+                                for (i, v) in self.enumerate() {
+                                    *ptr.offset(i as isize) = v.to_integer();
+                                }
+                            }
+                            LGLSXP => {
+                                let ptr = LOGICAL(sexp);
+                                for (i, v) in self.enumerate() {
+                                    *ptr.offset(i as isize) = v.to_logical();
+                                }
+                            }
+                            RAWSXP => {
+                                let ptr = RAW(sexp);
+                                for (i, v) in self.enumerate() {
+                                    *ptr.offset(i as isize) = v.to_raw();
+                                }
+                            }
+                            STRSXP => {
+                                for (i, v) in self.enumerate() {
+                                    SET_STRING_ELT(sexp, i as isize, v.to_sexp());
+                                }
+                            }
+                            _ => {
+                                panic!("unexpected SEXPTYPE in collect_robj");
+                            }
+                        }
+                        return Robj::Owned(sexp);
+                    } else {
+                        return Robj::from(());
+                    }
+                }
+            }
+
+            // If the size is indeterminate, create a vector and call recursively.
+            let vec: Vec<_> = self.collect();
+            assert!(vec.iter().size_hint() == (vec.len(), Some(vec.len())));
+            vec.into_iter().collect_robj()
+        }
+    }
+}
+
+// Thanks to *pretzelhammer* on stackoverflow for this.
+impl<T> RobjItertools for T where T: Iterator {}
+
+// Scalars which are ToVectorValue
+impl<T> From<T> for Robj
+where
+    T: ToVectorValue,
+{
+    fn from(scalar: T) -> Self {
+        Some(scalar).into_iter().collect_robj()
+    }
+}
+
+// We would love to do a blanket IntoIterator impl.
+// But the matching rules would clash with the above.
+macro_rules! impl_from_iter {
+    ($t: ty) => {
+        impl<'a, T> From<$t> for Robj
+        where
+            Self: 'a,
+            T: Clone + 'a,
+            T: ToVectorValue,
+        {
+            fn from(val: $t) -> Self {
+                val.iter().cloned().collect_robj()
+            }
+        }
+    };
+}
+
+macro_rules! impl_from_into_iter {
+    ($t: ty) => {
+        impl<'a, T> From<$t> for Robj
+        where
+            Self: 'a,
+            T: 'a,
+            &'a T: ToVectorValue,
+        {
+            fn from(val: $t) -> Self {
+                val.into_iter().collect_robj()
+            }
+        }
+    };
+}
+
+macro_rules! impl_from_as_iterator {
+    ($t: ty) => {
+        impl<T> From<$t> for Robj
+        where
+            $t: RobjItertools,
+            <$t as Iterator>::Item: ToVectorValue,
+            T: ToVectorValue,
+        {
+            fn from(val: $t) -> Self {
+                val.collect_robj()
+            }
+        }
+    };
+}
+
+// impl<T> From<Range<T>> for Robj
+// where
+//     Range<T> : RobjItertools,
+//     <Range<T> as Iterator>::Item: ToVectorValue,
+//     T : ToVectorValue
+// {
+//     fn from(val: Range<T>) -> Self {
+//         val.collect_robj()
+//     }
+// }
+
+// Template constants are still unstable in rust.
+impl_from_iter! {[T; 1]}
+impl_from_iter! {[T; 2]}
+impl_from_iter! {[T; 3]}
+impl_from_iter! {[T; 4]}
+impl_from_iter! {[T; 5]}
+impl_from_iter! {[T; 6]}
+impl_from_iter! {[T; 7]}
+impl_from_iter! {[T; 8]}
+impl_from_iter! {[T; 9]}
+impl_from_iter! {[T; 10]}
+impl_from_iter! {[T; 11]}
+impl_from_iter! {[T; 12]}
+impl_from_iter! {[T; 13]}
+impl_from_iter! {[T; 14]}
+impl_from_iter! {[T; 15]}
+impl_from_iter! {[T; 16]}
+impl_from_iter! {[T; 17]}
+impl_from_iter! {[T; 18]}
+impl_from_iter! {[T; 19]}
+impl_from_iter! {Vec<T>}
+
+impl_from_into_iter! {&'a [T]}
+
+impl_from_as_iterator! {Range<T>}
+impl_from_as_iterator! {RangeInclusive<T>}
diff --git a/extendr-api/src/robj/iter.rs b/extendr-api/src/robj/iter.rs
new file mode 100644
index 000000000..bba5eca96
--- /dev/null
+++ b/extendr-api/src/robj/iter.rs
@@ -0,0 +1,305 @@
+use super::*;
+
+// Iterator over the objects in a vector or string.
+#[derive(Clone)]
+pub struct VecIter {
+    vector: SEXP,
+    i: usize,
+    len: usize,
+}
+
+impl Iterator for VecIter {
+    type Item = Robj;
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.len, Some(self.len))
+    }
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let i = self.i;
+        self.i += 1;
+        if i >= self.len {
+            return None;
+        } else {
+            Some(Robj::from(unsafe { VECTOR_ELT(self.vector, i as isize) }))
+        }
+    }
+
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        self.i += n;
+        self.next()
+    }
+}
+
+impl std::fmt::Debug for VecIter {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "[")?;
+        for s in self.clone() {
+            write!(f, "{:?}", s)?;
+        }
+        write!(f, "]")
+    }
+}
+
+/// Iterator over name-value pairs in lists.
+pub type NamedListIter = std::iter::Zip<StrIter, VecIter>;
+
+/// Iterator over primitives in integer objects.
+pub type IntegerIter<'a> = std::slice::Iter<'a, i32>;
+
+/// Iterator over primitives in real objects.
+pub type RealIter<'a> = std::slice::Iter<'a, f64>;
+
+/// Iterator over primitives in logical objects.
+pub type LogicalIter<'a> = std::slice::Iter<'a, Bool>;
+
+/// Iterator over the objects in a vector or string.
+///
+/// ```
+/// use extendr_api::*;        // Put API in scope.
+/// extendr_engine::start_r(); // Start test environment.
+///
+/// let my_list = list!(a = 1, b = 2);
+/// let mut total = 0;
+/// for robj in my_list.list_iter().unwrap() {
+///   if let Some(val) = robj.as_integer() {
+///     total += val;
+///   }
+/// }
+/// assert_eq!(total, 3);
+///
+/// for name in my_list.names().unwrap() {
+///    assert!(name == "a" || name == "b")
+/// }
+/// ```
+#[derive(Clone)]
+pub struct ListIter {
+    list_elem: SEXP,
+}
+
+impl ListIter {
+    /// Make an empty list iterator.
+    pub fn new() -> Self {
+        unsafe {
+            Self {
+                list_elem: R_NilValue,
+            }
+        }
+    }
+}
+
+impl Iterator for ListIter {
+    type Item = Robj;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            let sexp = self.list_elem;
+            if sexp == R_NilValue {
+                None
+            } else {
+                self.list_elem = CDR(sexp);
+                Some(new_borrowed(CAR(sexp)))
+            }
+        }
+    }
+}
+
+impl std::fmt::Debug for ListIter {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "[")?;
+        for s in self.clone() {
+            write!(f, "{:?}", s)?;
+        }
+        write!(f, "]")
+    }
+}
+/// Iterator over strings or string factors.
+///
+/// ```
+/// use extendr_api::*;        // Put API in scope.
+/// extendr_engine::start_r(); // Start test environment.
+///
+/// let robj = r!(["a", "b", "c"]);
+/// assert_eq!(robj.str_iter().unwrap().collect::<Vec<_>>(), vec!["a", "b", "c"]);
+///
+/// let factor = factor!(["abcd", "def", "fg", "fg"]);
+/// assert_eq!(factor.levels().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg"]);
+/// assert_eq!(factor.as_integer_vector().unwrap(), vec![1, 2, 3, 3]);
+/// assert_eq!(factor.str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
+/// assert_eq!(factor.str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
+/// ```
+#[derive(Clone)]
+pub struct StrIter {
+    vector: SEXP,
+    i: usize,
+    len: usize,
+    levels: SEXP,
+}
+
+impl StrIter {
+    /// Make an empty str iterator.
+    pub fn new() -> Self {
+        unsafe {
+            Self {
+                vector: R_NilValue,
+                i: 0,
+                len: 0,
+                levels: R_NilValue,
+            }
+        }
+    }
+}
+
+// Get a string reference from a CHARSXP
+fn str_from_strsxp<'a>(sexp: SEXP, index: isize) -> &'a str {
+    unsafe {
+        if index < 0 || index >= Rf_xlength(sexp) {
+            ""
+        } else {
+            let charsxp = STRING_ELT(sexp, index);
+            let ptr = R_CHAR(charsxp) as *const u8;
+            let slice = std::slice::from_raw_parts(ptr, Rf_xlength(charsxp) as usize);
+            std::str::from_utf8_unchecked(slice)
+        }
+    }
+}
+
+impl Iterator for StrIter {
+    type Item = &'static str;
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.len, Some(self.len))
+    }
+
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            let i = self.i;
+            self.i += 1;
+            if i >= self.len {
+                return None;
+            } else if TYPEOF(self.vector) as u32 == STRSXP {
+                Some(str_from_strsxp(self.vector, i as isize))
+            } else if TYPEOF(self.vector) as u32 == INTSXP && TYPEOF(self.levels) as u32 == STRSXP {
+                let j = *(INTEGER(self.vector).offset(i as isize));
+                Some(str_from_strsxp(self.levels, j as isize - 1))
+            } else {
+                return None;
+            }
+        }
+    }
+
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        self.i += n;
+        self.next()
+    }
+}
+
+impl std::fmt::Debug for StrIter {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "[")?;
+        let mut comma = "";
+        for s in self.clone() {
+            write!(f, "{}{:?}", comma, s)?;
+            comma = ", ";
+        }
+        write!(f, "]")
+    }
+}
+
+impl Robj {
+    /// Get an iterator over a pairlist, lang or ... object.
+    /// Note: we plan to combine list iterators into one.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let mut robj = lang!("+", 1, 2);
+    /// let objects : Vec<_> = robj.pairlist_iter().unwrap().collect();
+    /// assert_eq!(objects, vec![r!(Symbol("+")), r!(1), r!(2)])
+    /// ```
+    pub fn pairlist_iter(&self) -> Option<ListIter> {
+        match self.sexptype() {
+            LISTSXP | LANGSXP | DOTSXP => unsafe {
+                Some(ListIter {
+                    list_elem: self.get(),
+                })
+            },
+            _ => None,
+        }
+    }
+
+    /// Get an iterator over an unnamed list.
+    /// Note: we plan to combine list iterators into one.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let mut robj = list!(1, 2, 3);
+    /// let objects : Vec<_> = robj.list_iter().unwrap().collect();
+    /// assert_eq!(objects, vec![r!(1), r!(2), r!(3)])
+    /// ```
+    pub fn list_iter(&self) -> Option<VecIter> {
+        match self.sexptype() {
+            VECSXP | EXPRSXP | WEAKREFSXP => unsafe {
+                Some(VecIter {
+                    vector: self.get(),
+                    i: 0,
+                    len: self.len(),
+                })
+            },
+            _ => None,
+        }
+    }
+
+    /// Get an iterator over a string vector.
+    /// Returns None if the object is not a string vector
+    /// but works for factors.
+    ///
+    /// ```
+    /// use extendr_api::*;
+    ///
+    /// extendr_engine::start_r();
+    ///
+    /// let obj = Robj::from(vec!["a", "b", "c"]);
+    /// assert_eq!(obj.str_iter().unwrap().collect::<Vec<_>>(), vec!["a", "b", "c"]);
+    ///
+    /// let factor = factor!(vec!["abcd", "def", "fg", "fg"]);
+    /// assert_eq!(factor.levels().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg"]);
+    /// assert_eq!(factor.as_integer_vector().unwrap(), vec![1, 2, 3, 3]);
+    /// assert_eq!(factor.str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
+    /// assert_eq!(factor.str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
+    /// ```
+    pub fn str_iter(&self) -> Option<StrIter> {
+        let i = 0;
+        let len = self.len();
+        match self.sexptype() {
+            STRSXP => unsafe {
+                let vector = self.get();
+                Some(StrIter {
+                    vector,
+                    i,
+                    len,
+                    levels: R_NilValue,
+                })
+            },
+            INTSXP => unsafe {
+                let vector = self.get();
+                if let Some(levels) = self.get_attrib(levels_symbol()) {
+                    if self.is_factor() && levels.sexptype() == STRSXP {
+                        Some(StrIter {
+                            vector,
+                            i,
+                            len,
+                            levels: levels.get(),
+                        })
+                    } else {
+                        None
+                    }
+                } else {
+                    None
+                }
+            },
+            _ => None,
+        }
+    }
+}
diff --git a/extendr-api/src/robj/mod.rs b/extendr-api/src/robj/mod.rs
new file mode 100644
index 000000000..d69ea5360
--- /dev/null
+++ b/extendr-api/src/robj/mod.rs
@@ -0,0 +1,1127 @@
+//! R object handling.
+//!
+//! See. https://cran.r-project.org/doc/manuals/R-exts.html
+//!
+//! Fundamental principals:
+//!
+//! * Any function that can break the protection mechanism is unsafe.
+//! * Users should be able to do almost everything without using libR_sys.
+//! * The interface should be friendly to R users without Rust experience.
+//!
+
+use libR_sys::*;
+use std::os::raw;
+
+use crate::logical::*;
+use crate::wrapper::*;
+use crate::{AnyError, IsNA};
+
+use std::collections::HashMap;
+use std::iter::IntoIterator;
+use std::ops::{Range, RangeInclusive};
+
+mod from_robj;
+mod into_robj;
+mod iter;
+mod rinternals;
+mod symbols;
+
+#[cfg(test)]
+mod tests;
+
+pub use from_robj::*;
+pub use into_robj::*;
+pub use iter::*;
+pub use rinternals::*;
+pub use symbols::*;
+
+/// Wrapper for an R S-expression pointer (SEXP).
+///
+/// Create R objects from rust types and iterators:
+///
+/// ```
+/// use extendr_api::*;        // Put API in scope.
+/// extendr_engine::start_r(); // Start test environment.
+///
+/// // Different ways of making integer scalar 1.
+/// let non_na : Option<i32> = Some(1);
+/// let a : Robj = vec![1].into();
+/// let b = r!(1);
+/// let c = r!(vec![1]);
+/// let d = r!(non_na);
+/// let e = r!([1]);
+/// assert_eq!(a, b);
+/// assert_eq!(a, c);
+/// assert_eq!(a, d);
+/// assert_eq!(a, e);
+///
+/// // Different ways of making boolean scalar TRUE.
+/// let a : Robj = true.into();
+/// let b = r!(TRUE);
+/// assert_eq!(a, b);
+///
+/// // Create a named list
+/// let a = list!(a = 1, b = "x");
+/// assert_eq!(a.len(), 2);
+///
+/// // Use an iterator (like 1:10)
+/// let a = r!(1 ..= 10);
+/// assert_eq!(a, r!([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
+///
+/// // Use an iterator (like (1:10)[(1:10) %% 3 == 0])
+/// let a = (1 ..= 10).filter(|v| v % 3 == 0).collect_robj();
+/// assert_eq!(a, c!(3, 6, 9));
+/// ```
+///
+/// Use iterators to get the contents of R objects.
+///
+/// ```
+/// use extendr_api::*;        // Put API in scope.
+/// extendr_engine::start_r(); // Start test environment.
+///
+/// let a : Robj = c!(1, 2, 3, 4, 5);
+/// let iter = a.integer_iter().unwrap();
+/// let robj = iter.filter(|&&x| x < 3).collect_robj();
+/// assert_eq!(robj, c!(1, 2));
+/// ```
+///
+/// Convert to/from Rust vectors.
+///
+/// ```
+/// use extendr_api::*;        // Put API in scope.
+/// extendr_engine::start_r(); // Start test environment.
+///
+/// let a : Robj = r!(vec![1., 2., 3., 4.]);
+/// let b : Vec<f64> = a.as_real_vector().unwrap();
+/// assert_eq!(a.len(), 4);
+/// assert_eq!(b, vec![1., 2., 3., 4.]);
+/// ```
+///
+/// Iterate over names and values.
+///
+/// ```
+/// use extendr_api::*;        // Put API in scope.
+/// extendr_engine::start_r(); // Start test environment.
+///
+/// let abc = list!(a = 1, b = "x", c = vec![1, 2]);
+/// let names : Vec<_> = abc.names().unwrap().collect();
+/// let names_and_values : Vec<_> = abc.named_list_iter().unwrap().collect();
+/// assert_eq!(names, vec!["a", "b", "c"]);
+/// assert_eq!(names_and_values, vec![("a", r!(1)), ("b", r!("x")), ("c", r!(vec![1, 2]))]);
+/// ```
+///
+/// NOTE: as much as possible we wish to make this object safe (ie. no segfaults).
+///
+/// If you avoid using unsafe functions it is more likely that you will avoid
+/// panics and segfaults. We will take great trouble to ensure that this
+/// is true.
+///
+pub enum Robj {
+    // This object owns the SEXP and must free it.
+    #[doc(hidden)]
+    Owned(SEXP),
+
+    //  This object references a SEXP such as an input parameter.
+    #[doc(hidden)]
+    Borrowed(SEXP),
+
+    //  This object references a SEXP owned by libR.
+    #[doc(hidden)]
+    Sys(SEXP),
+}
+
+/// TRUE value eg. `r!(TRUE)`
+pub const TRUE: Bool = Bool(1);
+
+/// FALSE value eg. `r!(FALSE)`
+pub const FALSE: Bool = Bool(0);
+
+/// NULL value eg. `r!(NULL)`
+pub const NULL: () = ();
+
+/// NA value for integers eg. `r!(NA_INTEGER)`
+pub const NA_INTEGER: Option<i32> = None;
+
+/// NA value for real values eg. `r!(NA_REAL)`
+pub const NA_REAL: Option<f64> = None;
+
+/// NA value for strings. `r!(NA_STRING)`
+pub const NA_STRING: Option<&str> = None;
+
+/// NA value for logical. `r!(NA_LOGICAL)`
+pub const NA_LOGICAL: Bool = Bool(std::i32::MIN);
+
+impl Clone for Robj {
+    fn clone(&self) -> Self {
+        self.duplicate()
+    }
+}
+
+impl Default for Robj {
+    fn default() -> Self {
+        Robj::from(())
+    }
+}
+
+impl Robj {
+    /// Get a copy of the underlying SEXP.
+    /// Note: this is unsafe.
+    #[doc(hidden)]
+    pub unsafe fn get(&self) -> SEXP {
+        match self {
+            Robj::Owned(sexp) => *sexp,
+            Robj::Borrowed(sexp) => *sexp,
+            Robj::Sys(sexp) => *sexp,
+        }
+    }
+
+    /// Get a copy of the underlying SEXP for mutable types.
+    /// This is valid only for owned objects as we are not
+    /// permitted to modify parameters or system objects.
+    #[doc(hidden)]
+    pub unsafe fn get_mut(&mut self) -> Option<SEXP> {
+        match self {
+            Robj::Owned(sexp) => Some(*sexp),
+            Robj::Borrowed(_) => None,
+            Robj::Sys(_) => None,
+        }
+    }
+
+    #[doc(hidden)]
+    /// Get the XXXSXP type of the object.
+    pub fn sexptype(&self) -> u32 {
+        unsafe { TYPEOF(self.get()) as u32 }
+    }
+
+    /// Get the extended length of the object.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let a : Robj = r!(vec![1., 2., 3., 4.]);
+    /// assert_eq!(a.len(), 4);
+    /// ```
+    pub fn len(&self) -> usize {
+        unsafe { Rf_xlength(self.get()) as usize }
+    }
+
+    /// Is this object is an NA scalar?
+    /// Works for character, integer and numeric types.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// assert_eq!(r!(NA_INTEGER).is_na(), true);
+    /// assert_eq!(r!(NA_REAL).is_na(), true);
+    /// assert_eq!(r!(NA_STRING).is_na(), true);
+    /// ```
+    pub fn is_na(&self) -> bool {
+        if self.len() != 1 {
+            false
+        } else {
+            unsafe {
+                let sexp = self.get();
+                match self.sexptype() {
+                    STRSXP => STRING_ELT(sexp, 0) == libR_sys::R_NaString,
+                    INTSXP => *(INTEGER(sexp)) == libR_sys::R_NaInt,
+                    LGLSXP => *(LOGICAL(sexp)) == libR_sys::R_NaInt,
+                    REALSXP => R_IsNA(*(REAL(sexp))) != 0,
+                    _ => false,
+                }
+            }
+        }
+    }
+
+    /// Get a read-only reference to the content of an integer vector.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([1, 2, 3]);
+    /// assert_eq!(robj.as_integer_slice().unwrap(), [1, 2, 3]);
+    /// ```
+    pub fn as_integer_slice<'a>(&self) -> Option<&'a [i32]> {
+        self.as_typed_slice()
+    }
+
+    /// Get an iterator over integer elements of this slice.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([1, 2, 3]);
+    /// let mut tot = 0;
+    /// for val in robj.integer_iter().unwrap() {
+    ///   tot += val;
+    /// }
+    /// assert_eq!(tot, 6);
+    /// ```
+    pub fn integer_iter<'a>(&self) -> Option<IntegerIter<'a>>
+    where
+        Self: 'a,
+    {
+        if let Some(slice) = self.as_integer_slice() {
+            Some(slice.iter())
+        } else {
+            None
+        }
+    }
+
+    /// Get a Vec<i32> copied from the object.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([1, 2, 3]);
+    /// assert_eq!(robj.as_integer_slice().unwrap(), vec![1, 2, 3]);
+    /// ```
+    pub fn as_integer_vector(&self) -> Option<Vec<i32>> {
+        if let Some(value) = self.as_integer_slice() {
+            Some(value.iter().cloned().collect::<Vec<_>>())
+        } else {
+            None
+        }
+    }
+
+    /// Get a read-only reference to the content of a logical vector
+    /// using the tri-state [Bool]. Returns None if not a logical vector.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([TRUE, FALSE, NA_LOGICAL]);
+    /// assert_eq!(robj.as_logical_slice().unwrap(), [TRUE, FALSE, NA_LOGICAL]);
+    /// ```
+    pub fn as_logical_slice(&self) -> Option<&[Bool]> {
+        self.as_typed_slice()
+    }
+
+    /// Get a Vec<Bool> copied from the object
+    /// using the tri-state [Bool].
+    /// Returns None if not a logical vector.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([TRUE, FALSE, NA_LOGICAL]);
+    /// assert_eq!(robj.as_logical_vector().unwrap(), vec![TRUE, FALSE, NA_LOGICAL]);
+    /// ```
+    pub fn as_logical_vector(&self) -> Option<Vec<Bool>> {
+        if let Some(value) = self.as_logical_slice() {
+            Some(value.iter().cloned().collect::<Vec<_>>())
+        } else {
+            None
+        }
+    }
+
+    /// Get an iterator over logical elements of this slice.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([TRUE, FALSE, NA_LOGICAL]);
+    /// let (mut nt, mut nf, mut nna) = (0, 0, 0);
+    /// for val in robj.logical_iter().unwrap() {
+    ///   match *val {
+    ///     TRUE => nt += 1,
+    ///     FALSE => nf += 1,
+    ///     NA_LOGICAL => nna += 1,
+    ///     _ => ()
+    ///   }
+    /// }
+    /// assert_eq!((nt, nf, nna), (1, 1, 1));
+    /// ```
+    pub fn logical_iter(&self) -> Option<LogicalIter> {
+        if let Some(slice) = self.as_logical_slice() {
+            Some(slice.iter())
+        } else {
+            None
+        }
+    }
+
+    /// Get a read-only reference to the content of a double vector.
+    /// Note: the slice may contain NaN or NA values.
+    /// We may introduce a "Real" type to handle this like the Bool type.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([Some(1.), None, Some(3.)]);
+    /// let mut tot = 0.;
+    /// for val in robj.as_real_slice().unwrap() {
+    ///   if !val.is_na() {
+    ///     tot += val;
+    ///   }
+    /// }
+    /// assert_eq!(tot, 4.);
+    /// ```
+    pub fn as_real_slice(&self) -> Option<&[f64]> {
+        self.as_typed_slice()
+    }
+
+    /// Get an iterator over real elements of this slice.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([1., 2., 3.]);
+    /// let mut tot = 0.;
+    /// for val in robj.real_iter().unwrap() {
+    ///   if !val.is_na() {
+    ///     tot += val;
+    ///   }
+    /// }
+    /// assert_eq!(tot, 6.);
+    /// ```
+    pub fn real_iter(&self) -> Option<RealIter> {
+        if let Some(slice) = self.as_real_slice() {
+            Some(slice.iter())
+        } else {
+            None
+        }
+    }
+
+    /// Get a Vec<f64> copied from the object.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([1., 2., 3.]);
+    /// assert_eq!(robj.as_real_vector().unwrap(), vec![1., 2., 3.])
+    /// ```
+    pub fn as_real_vector(&self) -> Option<Vec<f64>> {
+        if let Some(value) = self.as_real_slice() {
+            Some(value.iter().cloned().collect::<Vec<_>>())
+        } else {
+            None
+        }
+    }
+
+    /// Get a read-only reference to the content of an integer or logical vector.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let robj = r!([1_u8, 2, 3]);
+    /// assert_eq!(robj.as_raw_slice().unwrap(), [1_u8, 2, 3]);
+    /// ```
+    pub fn as_raw_slice(&self) -> Option<&[u8]> {
+        self.as_typed_slice()
+    }
+
+    /// Get a read-write reference to the content of an integer or logical vector.
+    /// Note that rust slices are 0-based so `slice[1]` is the middle value.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let mut robj = r!([1, 2, 3]);
+    /// let slice : & mut [i32] = robj.as_integer_slice_mut().unwrap();
+    /// slice[1] = 100;
+    /// assert_eq!(robj, r!([1, 100, 3]));
+    /// ```
+    pub fn as_integer_slice_mut(&mut self) -> Option<&mut [i32]> {
+        self.as_typed_slice_mut()
+    }
+
+    /// Get a read-write reference to the content of a double vector.
+    /// Note that rust slices are 0-based so `slice[1]` is the middle value.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let mut robj = r!([1.0, 2.0, 3.0]);
+    /// let slice = robj.as_real_slice_mut().unwrap();
+    /// slice[1] = 100.0;
+    /// assert_eq!(robj, r!([1.0, 100.0, 3.0]));
+    /// ```
+    pub fn as_real_slice_mut(&mut self) -> Option<&mut [f64]> {
+        self.as_typed_slice_mut()
+    }
+
+    /// Get a read-write reference to the content of a raw vector.
+    /// ```
+    /// use extendr_api::*;        // Put API in scope.
+    /// extendr_engine::start_r(); // Start test environment.
+    ///
+    /// let mut robj = r!([1_u8, 2, 3]);
+    /// let slice = robj.as_raw_slice_mut().unwrap();
+    /// slice[1] = 100;
+    /// assert_eq!(robj, r!([1_u8, 100, 3]));
+    /// ```
+    pub fn as_raw_slice_mut(&mut self) -> Option<&mut [u8]> {
+        self.as_typed_slice_mut()
+    }
+
+    /// Get a vector of owned strings.
+    /// Owned strings have long lifetimes, but are much slower than references.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let robj1 = Robj::from("xyz");
+    ///    assert_eq!(robj1.as_string_vector(), Some(vec!["xyz".to_string()]));
+    ///    let robj2 = Robj::from(1);
+    ///    assert_eq!(robj2.as_string_vector(), None);
+    /// ```
+    pub fn as_string_vector(&self) -> Option<Vec<String>> {
+        if let Some(iter) = self.str_iter() {
+            Some(iter.map(str::to_string).collect())
+        } else {
+            None
+        }
+    }
+
+    /// Get a vector of string references.
+    /// String references (&str) are faster, but have short lifetimes.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let robj1 = Robj::from("xyz");
+    ///    assert_eq!(robj1.as_str_vector(), Some(vec!["xyz"]));
+    ///    let robj2 = Robj::from(1);
+    ///    assert_eq!(robj2.as_str_vector(), None);
+    /// ```
+    pub fn as_str_vector(&self) -> Option<Vec<&str>> {
+        if let Some(iter) = self.str_iter() {
+            Some(iter.collect())
+        } else {
+            None
+        }
+    }
+
+    /// Get a read-only reference to a char, symbol or string type.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let robj1 = Robj::from("xyz");
+    ///    let robj2 = Robj::from(1);
+    ///    assert_eq!(robj1.as_str(), Some("xyz"));
+    ///    assert_eq!(robj2.as_str(), None);
+    /// ```
+    pub fn as_str(&self) -> Option<&str> {
+        unsafe {
+            match self.sexptype() {
+                STRSXP => {
+                    if self.len() != 1 {
+                        None
+                    } else {
+                        Some(to_str(R_CHAR(STRING_ELT(self.get(), 0)) as *const u8))
+                    }
+                }
+                CHARSXP => Some(to_str(R_CHAR(self.get()) as *const u8)),
+                SYMSXP => Some(to_str(R_CHAR(PRINTNAME(self.get())) as *const u8)),
+                _ => None,
+            }
+        }
+    }
+
+    /// Get a scalar integer.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let robj1 = Robj::from("xyz");
+    ///    let robj2 = Robj::from(1);
+    ///    let robj3 = Robj::from(NA_INTEGER);
+    ///    assert_eq!(robj1.as_integer(), None);
+    ///    assert_eq!(robj2.as_integer(), Some(1));
+    ///    assert_eq!(robj3.as_integer(), None);
+    /// ```
+    pub fn as_integer(&self) -> Option<i32> {
+        match self.as_integer_slice() {
+            Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0]),
+            _ => None,
+        }
+    }
+
+    /// Get a scalar real.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let robj1 = Robj::from(1);
+    ///    let robj2 = Robj::from(1.);
+    ///    let robj3 = Robj::from(NA_REAL);
+    ///    assert_eq!(robj1.as_real(), None);
+    ///    assert_eq!(robj2.as_real(), Some(1.));
+    ///    assert_eq!(robj3.as_real(), None);
+    /// ```
+    pub fn as_real(&self) -> Option<f64> {
+        match self.as_real_slice() {
+            Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0]),
+            _ => None,
+        }
+    }
+
+    /// Get a scalar rust boolean.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let robj1 = Robj::from(TRUE);
+    ///    let robj2 = Robj::from(1.);
+    ///    let robj3 = Robj::from(NA_LOGICAL);
+    ///    assert_eq!(robj1.as_bool(), Some(true));
+    ///    assert_eq!(robj2.as_bool(), None);
+    ///    assert_eq!(robj3.as_bool(), None);
+    /// ```
+    pub fn as_bool(&self) -> Option<bool> {
+        match self.as_logical_slice() {
+            Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0].into()),
+            _ => None,
+        }
+    }
+
+    /// Get a scalar boolean as a tri-boolean [Bool] value.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let robj1 = Robj::from(TRUE);
+    ///    let robj2 = Robj::from([TRUE, FALSE]);
+    ///    let robj3 = Robj::from(NA_LOGICAL);
+    ///    assert_eq!(robj1.as_logical(), Some(TRUE));
+    ///    assert_eq!(robj2.as_logical(), None);
+    ///    assert_eq!(robj3.as_logical(), Some(NA_LOGICAL));
+    /// ```
+    pub fn as_logical(&self) -> Option<Bool> {
+        match self.as_logical_slice() {
+            Some(slice) if slice.len() == 1 => Some(slice[0]),
+            _ => None,
+        }
+    }
+
+    /// Evaluate the expression in R and return an error or an R object.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let add = lang!("+", 1, 2);
+    ///    assert_eq!(add.eval().unwrap(), r!(3));
+    /// ```
+    pub fn eval(&self) -> Result<Robj, AnyError> {
+        unsafe {
+            let mut error: raw::c_int = 0;
+            let res = R_tryEval(self.get(), R_GlobalEnv, &mut error as *mut raw::c_int);
+            if error != 0 {
+                Err(AnyError::from("R eval error"))
+            } else {
+                Ok(Robj::from(res))
+            }
+        }
+    }
+
+    /// Evaluate the expression and return NULL or an R object.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let bad = lang!("imnotavalidfunctioninR", 1, 2);
+    ///    assert_eq!(bad.eval_blind(), r!(NULL));
+    /// ```
+    pub fn eval_blind(&self) -> Robj {
+        let res = self.eval();
+        if res.is_err() {
+            Robj::from(())
+        } else {
+            Robj::from(res.unwrap())
+        }
+    }
+
+    /// Parse a string into an R executable object
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let expr = Robj::parse("1 + 2").unwrap();
+    ///    assert!(expr.is_expression());
+    /// ```
+    pub fn parse(code: &str) -> Result<Robj, AnyError> {
+        unsafe {
+            use libR_sys::*;
+            let mut status = 0_u32;
+            let status_ptr = &mut status as *mut u32;
+            let code: Robj = code.into();
+            let parsed = Robj::from(R_ParseVector(code.get(), -1, status_ptr, R_NilValue));
+            match status {
+                1 => Ok(parsed),
+                _ => Err(AnyError::from("parse_error")),
+            }
+        }
+    }
+
+    /// Parse a string into an R executable object and run it.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let res = Robj::eval_string("1 + 2").unwrap();
+    ///    assert_eq!(res, r!(3.));
+    /// ```
+    pub fn eval_string(code: &str) -> Result<Robj, AnyError> {
+        let expr = Robj::parse(code)?;
+        let mut res = Robj::from(());
+        if let Some(iter) = expr.list_iter() {
+            for lang in iter {
+                res = lang.eval()?;
+            }
+        }
+        Ok(res)
+    }
+
+    /// Unprotect an object - assumes a transfer of ownership.
+    /// This is unsafe because the object pointer may be left dangling.
+    #[doc(hidden)]
+    pub unsafe fn unprotected(self) -> Robj {
+        match self {
+            Robj::Owned(sexp) => {
+                R_ReleaseObject(sexp);
+                Robj::Borrowed(sexp)
+            }
+            _ => self,
+        }
+    }
+
+    /// Return true if the object is owned by this wrapper.
+    /// If so, it will be released when the wrapper drops.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///    let owned = r!(1);      // Allocated vector.
+    ///    let borrowed = r!(());  // R_NilValue
+    ///    assert_eq!(owned.is_owned(), true);
+    ///    assert_eq!(borrowed.is_owned(), false);
+    /// ```
+    pub fn is_owned(&self) -> bool {
+        match self {
+            Robj::Owned(_) => true,
+            _ => false,
+        }
+    }
+
+    // Convert the Robj to an owned one.
+    #[doc(hidden)]
+    pub fn to_owned(self) -> Robj {
+        match self {
+            Robj::Owned(_) => self,
+            _ => unsafe { new_owned(self.get()) },
+        }
+    }
+}
+
+/// Generic access to typed slices in an Robj.
+pub trait AsTypedSlice<'a, T>
+where
+    Self: 'a,
+{
+    fn as_typed_slice(&self) -> Option<&'a [T]>
+    where
+        Self: 'a,
+    {
+        None
+    }
+
+    fn as_typed_slice_mut(&mut self) -> Option<&'a mut [T]>
+    where
+        Self: 'a,
+    {
+        None
+    }
+}
+
+macro_rules! make_typed_slice {
+    ($type: ty, $fn: tt, $($sexp: tt),* ) => {
+        impl<'a> AsTypedSlice<'a, $type> for Robj
+        where
+            Self : 'a,
+        {
+            fn as_typed_slice(&self) -> Option<&'a [$type]> {
+                match self.sexptype() {
+                    $( $sexp )|* => {
+                        unsafe {
+                            let ptr = $fn(self.get()) as *const $type;
+                            Some(std::slice::from_raw_parts(ptr, self.len()))
+                        }
+                    }
+                    _ => None
+                }
+            }
+
+            fn as_typed_slice_mut(&mut self) -> Option<&'a mut [$type]> {
+                match self.sexptype() {
+                    $( $sexp )|* => {
+                        unsafe {
+                            let ptr = $fn(self.get()) as *mut $type;
+                            Some(std::slice::from_raw_parts_mut(ptr, self.len()))
+                        }
+                    }
+                    _ => None
+                }
+            }
+        }
+    }
+}
+
+make_typed_slice!(Bool, INTEGER, LGLSXP);
+make_typed_slice!(i32, INTEGER, INTSXP);
+make_typed_slice!(f64, REAL, REALSXP);
+make_typed_slice!(u8, RAW, RAWSXP);
+
+/// These are helper functions which give access to common properties of R objects.
+#[allow(non_snake_case)]
+impl Robj {
+    /// Get a specific attribute as a borrowed robj if it exists.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let mut robj = r!("hello");
+    ///    robj.set_attrib(Symbol("xyz"), 1);
+    ///    assert_eq!(robj.get_attrib(Symbol("xyz")), Some(r!(1)));
+    /// ```
+    pub fn get_attrib<'a, N>(&self, name: N) -> Option<Robj>
+    where
+        Self: 'a,
+        Robj: From<N> + 'a,
+    {
+        let name = Robj::from(name);
+        if self.sexptype() == CHARSXP {
+            None
+        } else {
+            let res = unsafe { new_borrowed(Rf_getAttrib(self.get(), name.get())) };
+            if res.is_null() {
+                None
+            } else {
+                Some(res)
+            }
+        }
+    }
+
+    /// Set a specific attribute and return the value.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let mut robj = r!("hello");
+    ///    let value = robj.set_attrib(Symbol("xyz"), 1);
+    ///    assert_eq!(robj.get_attrib(Symbol("xyz")), Some(value));
+    /// ```
+    pub fn set_attrib<N, V>(&mut self, name: N, value: V) -> Robj
+    where
+        Robj: From<N>,
+        Robj: From<V>,
+    {
+        let name = Robj::from(name);
+        let value = Robj::from(value);
+        unsafe { new_borrowed(Rf_setAttrib(self.get(), name.get(), value.get())) }
+    }
+
+    /// Get the names attribute as a string iterator if one exists.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let list = list!(a = 1, b = 2, c = 3);
+    ///    let names : Vec<_> = list.names().unwrap().collect();
+    ///    assert_eq!(names, vec!["a", "b", "c"]);
+    /// ```
+    pub fn names(&self) -> Option<StrIter> {
+        if let Some(names) = self.get_attrib(names_symbol()) {
+            names.str_iter()
+        } else {
+            None
+        }
+    }
+
+    /// Get the dim attribute as an integer iterator if one exists.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let array = R!(array(data = c(1, 2, 3, 4), dim = c(2, 2), dimnames = list(c("x", "y"), c("a","b")))).unwrap();
+    ///    let dim : Vec<_> = array.dim().unwrap().cloned().collect();
+    ///    assert_eq!(dim, vec![2, 2]);
+    /// ```
+    pub fn dim<'a>(&self) -> Option<IntegerIter<'a>>
+    where
+        Self: 'a,
+        Robj: 'a,
+    {
+        if let Some(dim) = self.get_attrib(dim_symbol()) {
+            dim.integer_iter()
+        } else {
+            None
+        }
+    }
+
+    /// Get the dimnames attribute as a list iterator if one exists.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let array = R!(array(data = c(1, 2, 3, 4), dim = c(2, 2), dimnames = list(c("x", "y"), c("a","b")))).unwrap();
+    ///    let names : Vec<_> = array.dimnames().unwrap().collect();
+    ///    assert_eq!(names, vec![r!(["x", "y"]), r!(["a", "b"])]);
+    /// ```
+    pub fn dimnames(&self) -> Option<VecIter> {
+        if let Some(names) = self.get_attrib(dimnames_symbol()) {
+            names.list_iter()
+        } else {
+            None
+        }
+    }
+
+    /// Return an iterator over names and values of a list if they exist.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let list = list!(a = 1, b = 2, c = 3);
+    ///    let names_and_values : Vec<_> = list.named_list_iter().unwrap().collect();
+    ///    assert_eq!(names_and_values, vec![("a", r!(1)), ("b", r!(2)), ("c", r!(3))]);
+    /// ```
+    pub fn named_list_iter(&self) -> Option<NamedListIter> {
+        if let Some(names) = self.names() {
+            if let Some(values) = self.list_iter() {
+                return Some(names.zip(values));
+            }
+        }
+        None
+    }
+
+    /// Get the class attribute as a string iterator if one exists.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let formula = R!(y ~ A * x + b).unwrap();
+    ///    let class : Vec<_> = formula.class().unwrap().collect();
+    ///    assert_eq!(class, ["formula"]);
+    /// ```
+    pub fn class(&self) -> Option<StrIter> {
+        if let Some(class) = self.get_attrib(class_symbol()) {
+            class.str_iter()
+        } else {
+            None
+        }
+    }
+
+    /// Return true if this class inherits this class.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let formula = R!(y ~ A * x + b).unwrap();
+    ///    assert_eq!(formula.inherits("formula"), true);
+    /// ```
+    pub fn inherits(&self, classname: &str) -> bool {
+        if let Some(mut iter) = self.class() {
+            iter.find(|&n| n == classname).is_some()
+        } else {
+            false
+        }
+    }
+
+    /// Get the levels attribute as a string iterator if one exists.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    let factor = factor!(vec!["abcd", "def", "fg", "fg"]);
+    ///    let levels : Vec<_> = factor.levels().unwrap().collect();
+    ///    assert_eq!(levels, vec!["abcd", "def", "fg"]);
+    /// ```
+    pub fn levels(&self) -> Option<StrIter> {
+        if let Some(levels) = self.get_attrib(levels_symbol()) {
+            levels.str_iter()
+        } else {
+            None
+        }
+    }
+}
+
+#[doc(hidden)]
+pub unsafe fn new_owned(sexp: SEXP) -> Robj {
+    R_PreserveObject(sexp);
+    Robj::Owned(sexp)
+}
+
+#[doc(hidden)]
+pub unsafe fn new_borrowed(sexp: SEXP) -> Robj {
+    Robj::Borrowed(sexp)
+}
+
+#[doc(hidden)]
+pub unsafe fn new_sys(sexp: SEXP) -> Robj {
+    Robj::Sys(sexp)
+}
+
+/// Compare equality with integer slices.
+impl<'a> PartialEq<[i32]> for Robj {
+    fn eq(&self, rhs: &[i32]) -> bool {
+        self.as_integer_slice() == Some(rhs)
+    }
+}
+
+/// Compare equality with slices of double.
+impl<'a> PartialEq<[f64]> for Robj {
+    fn eq(&self, rhs: &[f64]) -> bool {
+        self.as_real_slice() == Some(rhs)
+    }
+}
+
+/// Compare equality with strings.
+impl PartialEq<str> for Robj {
+    fn eq(&self, rhs: &str) -> bool {
+        self.as_str() == Some(rhs)
+    }
+}
+
+/// Compare equality with two Robjs.
+impl PartialEq<Robj> for Robj {
+    fn eq(&self, rhs: &Robj) -> bool {
+        if self.sexptype() == rhs.sexptype() && self.len() == rhs.len() {
+            unsafe {
+                let lsexp = self.get();
+                let rsexp = rhs.get();
+                match self.sexptype() {
+                    NILSXP => true,
+                    SYMSXP => PRINTNAME(lsexp) == PRINTNAME(rsexp),
+                    LISTSXP | LANGSXP | DOTSXP => self
+                        .pairlist_iter()
+                        .unwrap()
+                        .eq(rhs.pairlist_iter().unwrap()),
+                    CLOSXP => false,
+                    ENVSXP => false,
+                    PROMSXP => false,
+                    SPECIALSXP => false,
+                    BUILTINSXP => false,
+                    CHARSXP => self.as_str() == rhs.as_str(),
+                    LGLSXP => self.as_logical_slice() == rhs.as_logical_slice(),
+                    INTSXP => self.as_integer_slice() == rhs.as_integer_slice(),
+                    REALSXP => self.as_real_slice() == rhs.as_real_slice(),
+                    CPLXSXP => false,
+                    ANYSXP => false,
+                    VECSXP | EXPRSXP => self.list_iter().unwrap().eq(rhs.list_iter().unwrap()),
+                    STRSXP => self.str_iter().unwrap().eq(rhs.str_iter().unwrap()),
+                    BCODESXP => false,
+                    EXTPTRSXP => false,
+                    WEAKREFSXP => false,
+                    RAWSXP => self.as_raw_slice() == rhs.as_raw_slice(),
+                    S4SXP => false,
+                    NEWSXP => false,
+                    FREESXP => false,
+                    _ => false,
+                }
+            }
+        } else {
+            false
+        }
+    }
+}
+
+/// Implement {:?} formatting.
+impl std::fmt::Debug for Robj {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self.sexptype() {
+            NILSXP => write!(f, "NULL"),
+            SYMSXP => write!(f, "Symbol({:?})", self.as_str().unwrap()),
+            // LISTSXP => false,
+            // CLOSXP => false,
+            // ENVSXP => false,
+            // PROMSXP => false,
+            LANGSXP => write!(
+                f,
+                "Lang({:?})",
+                self.pairlist_iter().unwrap().collect::<Vec<Robj>>()
+            ),
+            // SPECIALSXP => false,
+            // BUILTINSXP => false,
+            CHARSXP => write!(f, "Character({:?})", self.as_str().unwrap()),
+            LGLSXP => {
+                let slice = self.as_logical_slice().unwrap();
+                if slice.len() == 1 {
+                    write!(f, "{}", if slice[0].0 == 0 { "FALSE" } else { "TRUE" })
+                } else {
+                    write!(f, "&{:?}", slice)
+                }
+            }
+            INTSXP => {
+                let slice = self.as_integer_slice().unwrap();
+                if slice.len() == 1 {
+                    write!(f, "{:?}", slice[0])
+                } else {
+                    write!(f, "{:?}", self.as_integer_slice().unwrap())
+                }
+            }
+            REALSXP => {
+                let slice = self.as_real_slice().unwrap();
+                if slice.len() == 1 {
+                    write!(f, "{:?}", slice[0])
+                } else {
+                    write!(f, "{:?}", slice)
+                }
+            }
+            VECSXP => write!(f, "{:?}", self.list_iter().unwrap().collect::<Vec<_>>()),
+            EXPRSXP => write!(
+                f,
+                "Expr({:?})",
+                self.list_iter().unwrap().collect::<Vec<_>>()
+            ),
+            WEAKREFSXP => write!(
+                f,
+                "Weakref({:?})",
+                self.list_iter().unwrap().collect::<Vec<_>>()
+            ),
+            // CPLXSXP => false,
+            STRSXP => {
+                write!(f, "[")?;
+                let mut sep = "";
+                for obj in self.str_iter().unwrap() {
+                    write!(f, "{}{:?}", sep, obj)?;
+                    sep = ", ";
+                }
+                write!(f, "]")
+            }
+            // DOTSXP => false,
+            // ANYSXP => false,
+            // VECSXP => false,
+            // EXPRSXP => false,
+            // BCODESXP => false,
+            // EXTPTRSXP => false,
+            // WEAKREFSXP => false,
+            RAWSXP => {
+                let slice = self.as_raw_slice().unwrap();
+                if slice.len() == 1 {
+                    write!(f, "{}", slice[0])
+                } else {
+                    write!(f, "{:?}", slice)
+                }
+            }
+            // S4SXP => false,
+            // NEWSXP => false,
+            // FREESXP => false,
+            _ => write!(f, "??"),
+        }
+    }
+}
+
+// Internal utf8 to str conversion.
+// Lets not worry about non-ascii/unicode strings for now (or ever).
+unsafe fn to_str<'a>(ptr: *const u8) -> &'a str {
+    let mut len = 0;
+    loop {
+        if *ptr.offset(len) == 0 {
+            break;
+        }
+        len += 1;
+    }
+    let slice = std::slice::from_raw_parts(ptr, len as usize);
+    std::str::from_utf8_unchecked(slice)
+}
+
+/// Release any owned objects.
+impl Drop for Robj {
+    fn drop(&mut self) {
+        unsafe {
+            match self {
+                Robj::Owned(sexp) => R_ReleaseObject(*sexp),
+                Robj::Borrowed(_) => (),
+                Robj::Sys(_) => (),
+            }
+        }
+    }
+}
diff --git a/extendr-api/src/robj/rinternals.rs b/extendr-api/src/robj/rinternals.rs
new file mode 100644
index 000000000..dcf1d7e83
--- /dev/null
+++ b/extendr-api/src/robj/rinternals.rs
@@ -0,0 +1,494 @@
+use crate::*;
+use super::*;
+
+///////////////////////////////////////////////////////////////
+/// The following impls wrap specific Rinternals.h functions.
+///
+#[allow(non_snake_case)]
+impl Robj {
+    /// Return true if this is the null object.
+    pub fn is_null(&self) -> bool {
+        unsafe { Rf_isNull(self.get()) != 0 }
+    }
+
+    /// Return true if this is a symbol.
+    pub fn is_symbol(&self) -> bool {
+        unsafe { Rf_isSymbol(self.get()) != 0 }
+    }
+
+    /// Return true if this is a boolean (logical) vector
+    pub fn is_logical(&self) -> bool {
+        unsafe { Rf_isLogical(self.get()) != 0 }
+    }
+
+    /// Return true if this is a real (f64) vector.
+    pub fn is_real(&self) -> bool {
+        unsafe { Rf_isReal(self.get()) != 0 }
+    }
+
+    /// Return true if this is a complex vector.
+    pub fn is_complex(&self) -> bool {
+        unsafe { Rf_isComplex(self.get()) != 0 }
+    }
+
+    /// Return true if this is an expression.
+    pub fn is_expression(&self) -> bool {
+        unsafe { Rf_isExpression(self.get()) != 0 }
+    }
+
+    /// Return true if this is an environment.
+    pub fn is_environment(&self) -> bool {
+        unsafe { Rf_isEnvironment(self.get()) != 0 }
+    }
+
+    /// Return true if this is a string.
+    pub fn is_string(&self) -> bool {
+        unsafe { Rf_isString(self.get()) != 0 }
+    }
+
+    /// Return true if this is an object.
+    pub fn is_object(&self) -> bool {
+        unsafe { Rf_isObject(self.get()) != 0 }
+    }
+
+    /// Get the source ref.
+    pub fn get_current_srcref(val: i32) -> Robj {
+        unsafe { new_owned(R_GetCurrentSrcref(val as raw::c_int)) }
+    }
+
+    /// Get the source filename.
+    pub fn get_src_filename(&self) -> Robj {
+        unsafe { new_owned(R_GetSrcFilename(self.get())) }
+    }
+
+    /// Convert to a string vector.
+    pub fn as_char(&self) -> Robj {
+        unsafe { new_owned(Rf_asChar(self.get())) }
+    }
+
+    /// Convert to vectors of many kinds.
+    pub fn coerce_vector(&self, sexptype: u32) -> Robj {
+        unsafe { new_owned(Rf_coerceVector(self.get(), sexptype as SEXPTYPE)) }
+    }
+
+    /// Convert a pairlist (LISTSXP) to a vector list (VECSXP).
+    pub fn pair_to_vector_list(&self) -> Robj {
+        unsafe { new_owned(Rf_PairToVectorList(self.get())) }
+    }
+
+    /// Convert a vector list (VECSXP) to a pair list (LISTSXP)
+    pub fn vector_to_pair_list(&self) -> Robj {
+        unsafe { new_owned(Rf_VectorToPairList(self.get())) }
+    }
+
+    /// Convert a factor to a string vector.
+    pub fn as_character_factor(&self) -> Robj {
+        unsafe { new_owned(Rf_asCharacterFactor(self.get())) }
+    }
+
+    /// Allocate a matrix object.
+    pub fn alloc_matrix(sexptype: SEXPTYPE, rows: i32, cols: i32) -> Robj {
+        unsafe { new_owned(Rf_allocMatrix(sexptype, rows, cols)) }
+    }
+
+    /* TODO:
+    int Rf_asLogical2(SEXP x, int checking, SEXP call, SEXP rho);
+    Rcomplex Rf_asComplex(SEXP x);
+    void Rf_addMissingVarsToNewEnv(SEXP, SEXP);
+    SEXP Rf_alloc3DArray(SEXPTYPE, int, int, int);
+    SEXP Rf_allocArray(SEXPTYPE, SEXP);
+    SEXP Rf_allocFormalsList2(SEXP sym1, SEXP sym2);
+    SEXP Rf_allocFormalsList3(SEXP sym1, SEXP sym2, SEXP sym3);
+    SEXP Rf_allocFormalsList4(SEXP sym1, SEXP sym2, SEXP sym3, SEXP sym4);
+    SEXP Rf_allocFormalsList5(SEXP sym1, SEXP sym2, SEXP sym3, SEXP sym4, SEXP sym5);
+    SEXP Rf_allocFormalsList6(SEXP sym1, SEXP sym2, SEXP sym3, SEXP sym4, SEXP sym5, SEXP sym6);
+    SEXP Rf_allocList(int);
+    SEXP Rf_allocS4Object(void);
+    SEXP Rf_allocSExp(SEXPTYPE);
+    SEXP Rf_allocVector3(SEXPTYPE, R_xlen_t, R_allocator_t*);
+    R_xlen_t Rf_any_duplicated(SEXP x, Rboolean from_last);
+    R_xlen_t Rf_any_duplicated3(SEXP x, SEXP incomp, Rboolean from_last);
+    SEXP Rf_applyClosure(SEXP, SEXP, SEXP, SEXP, SEXP);
+    SEXP Rf_arraySubscript(int, SEXP, SEXP, SEXP (*)(SEXP,SEXP), SEXP (*)(SEXP, int), SEXP);
+    SEXP Rf_classgets(SEXP, SEXP);
+    SEXP Rf_cons(SEXP, SEXP);
+    SEXP Rf_fixSubset3Args(SEXP, SEXP, SEXP, SEXP*);
+    void Rf_copyMatrix(SEXP, SEXP, Rboolean);
+    void Rf_copyListMatrix(SEXP, SEXP, Rboolean);
+    void Rf_copyMostAttrib(SEXP, SEXP);
+    void Rf_copyVector(SEXP, SEXP);
+    int Rf_countContexts(int, int);
+    SEXP Rf_CreateTag(SEXP);
+    void Rf_defineVar(SEXP, SEXP, SEXP);
+    SEXP Rf_dimgets(SEXP, SEXP);
+    SEXP Rf_dimnamesgets(SEXP, SEXP);
+    SEXP Rf_DropDims(SEXP);
+    */
+
+    /// Compatible way to duplicate an object. Use obj.clone() instead
+    /// for Rust compatibility.
+    pub fn duplicate(&self) -> Self {
+        unsafe { new_owned(Rf_duplicate(self.get())) }
+    }
+
+    /*
+    SEXP Rf_shallow_duplicate(SEXP);
+    SEXP R_duplicate_attr(SEXP);
+    SEXP R_shallow_duplicate_attr(SEXP);
+    SEXP Rf_lazy_duplicate(SEXP);
+    SEXP Rf_duplicated(SEXP, Rboolean);
+    Rboolean R_envHasNoSpecialSymbols(SEXP);
+    SEXP Rf_eval(SEXP, SEXP);
+    SEXP Rf_ExtractSubset(SEXP, SEXP, SEXP);
+    SEXP Rf_findFun(SEXP, SEXP);
+    SEXP Rf_findFun3(SEXP, SEXP, SEXP);
+    void Rf_findFunctionForBody(SEXP);
+    SEXP Rf_findVar(SEXP, SEXP);
+    SEXP Rf_findVarInFrame(SEXP, SEXP);
+    SEXP Rf_findVarInFrame3(SEXP, SEXP, Rboolean);
+    */
+
+    /// Find a function in an environment ignoring other variables.
+    /// ```
+    ///    use extendr_api::*;
+    ///    extendr_engine::start_r();
+    ///
+    ///    R!(my_fun <- function() {});
+    ///    let my_fun = global_env().find_function(Symbol("my_fun")).unwrap();
+    ///    assert_eq!(my_fun.is_function(), true);
+    /// ```
+    pub fn find_function<S>(&self, symbol: S) -> Result<Robj, AnyError>
+    where
+        Robj: From<S>,
+    {
+        let symbol = Robj::from(symbol);
+        if !symbol.is_symbol() {
+            return Err("find_fun needs a Symbol. eg. find_fun(Symbol(\"xyz\"))".into());
+        }
+        if !self.is_environment() {
+            return Err("find_fun needs an environment.".into());
+        }
+        Ok(unsafe { new_borrowed(Rf_findFun(symbol.get(), self.get())) })
+    }
+
+    /*
+    SEXP Rf_GetArrayDimnames(SEXP);
+    SEXP Rf_GetColNames(SEXP);
+    void Rf_GetMatrixDimnames(SEXP, SEXP*, SEXP*, const char**, const char**);
+    SEXP Rf_GetOption(SEXP, SEXP);
+    SEXP Rf_GetOption1(SEXP);
+    int Rf_FixupDigits(SEXP, warn_type);
+    int Rf_FixupWidth (SEXP, warn_type);
+    int Rf_GetOptionDigits(void);
+    int Rf_GetOptionWidth(void);
+    SEXP Rf_GetRowNames(SEXP);
+    void Rf_gsetVar(SEXP, SEXP, SEXP);
+    SEXP Rf_install(const char *);
+    SEXP Rf_installChar(SEXP);
+    SEXP Rf_installNoTrChar(SEXP);
+    SEXP Rf_installTrChar(SEXP);
+    SEXP Rf_installDDVAL(int i);
+    SEXP Rf_installS3Signature(const char *, const char *);
+    Rboolean Rf_isFree(SEXP);
+    Rboolean Rf_isOrdered(SEXP);
+    Rboolean Rf_isUnmodifiedSpecSym(SEXP sym, SEXP env);
+    Rboolean Rf_isUnordered(SEXP);
+    Rboolean Rf_isUnsorted(SEXP, Rboolean);
+    SEXP Rf_lengthgets(SEXP, R_len_t);
+    SEXP Rf_xlengthgets(SEXP, R_xlen_t);
+    SEXP R_lsInternal(SEXP, Rboolean);
+    SEXP R_lsInternal3(SEXP, Rboolean, Rboolean);
+    SEXP Rf_match(SEXP, SEXP, int);
+    SEXP Rf_matchE(SEXP, SEXP, int, SEXP);
+    SEXP Rf_namesgets(SEXP, SEXP);
+    SEXP Rf_mkChar(const char *);
+    SEXP Rf_mkCharLen(const char *, int);
+    Rboolean Rf_NonNullStringMatch(SEXP, SEXP);
+    */
+
+    /// Number of columns of a matrix
+    pub fn ncols(&self) -> usize {
+        unsafe { Rf_ncols(self.get()) as usize }
+    }
+
+    /// Number of rows of a matrix
+    pub fn nrows(&self) -> usize {
+        unsafe { Rf_nrows(self.get()) as usize }
+    }
+
+    /*SEXP Rf_nthcdr(SEXP, int);
+    Rboolean Rf_pmatch(SEXP, SEXP, Rboolean);
+    Rboolean Rf_psmatch(const char *, const char *, Rboolean);
+    void Rf_PrintValue(SEXP);
+    void Rf_printwhere(void);
+    void Rf_readS3VarsFromFrame(SEXP, SEXP*, SEXP*, SEXP*, SEXP*, SEXP*, SEXP*);
+    SEXP Rf_setAttrib(SEXP, SEXP, SEXP);
+    void Rf_setSVector(SEXP*, int, SEXP);
+    void Rf_setVar(SEXP, SEXP, SEXP);
+    SEXP Rf_stringSuffix(SEXP, int);
+    SEXPTYPE Rf_str2type(const char *);
+    Rboolean Rf_StringBlank(SEXP);
+    SEXP Rf_substitute(SEXP,SEXP);
+    SEXP Rf_topenv(SEXP, SEXP);
+    const char * Rf_translateChar(SEXP);
+    const char * Rf_translateChar0(SEXP);
+    const char * Rf_translateCharUTF8(SEXP);
+    const char * Rf_type2char(SEXPTYPE);
+    SEXP Rf_type2rstr(SEXPTYPE);
+    SEXP Rf_type2str(SEXPTYPE);
+    SEXP Rf_type2str_nowarn(SEXPTYPE);
+    SEXP R_GetCurrentEnv();
+    Rboolean Rf_isS4(SEXP);
+    SEXP Rf_asS4(SEXP, Rboolean, int);
+    SEXP Rf_S3Class(SEXP);
+    int Rf_isBasicClass(const char *);
+    Rboolean R_cycle_detected(SEXP s, SEXP child);
+    u32 Rf_getCharCE(SEXP);
+    SEXP Rf_mkCharCE(const char *, cetype_t);
+    SEXP Rf_mkCharLenCE(const char *, int, cetype_t);
+    SEXP R_forceAndCall(SEXP e, int n, SEXP rho);
+    */
+
+    #[doc(hidden)]
+    pub unsafe fn makeExternalPtr<T>(p: *mut T, tag: Robj, prot: Robj) -> Self {
+        Robj::make_external_ptr(p, tag, prot)
+    }
+
+    /// Internal function used to implement `#[extendr]` impl
+    #[doc(hidden)]
+    pub unsafe fn make_external_ptr<T>(p: *mut T, tag: Robj, prot: Robj) -> Self {
+        new_owned(R_MakeExternalPtr(
+            p as *mut ::std::os::raw::c_void,
+            tag.get(),
+            prot.get(),
+        ))
+    }
+
+    #[doc(hidden)]
+    pub unsafe fn externalPtrAddr<T>(&self) -> *mut T {
+        R_ExternalPtrAddr(self.get()) as *mut T
+    }
+
+    /// Internal function used to implement `#[extendr]` impl
+    #[doc(hidden)]
+    pub unsafe fn external_ptr_addr<T>(&self) -> *mut T {
+        R_ExternalPtrAddr(self.get()) as *mut T
+    }
+
+    #[doc(hidden)]
+    pub unsafe fn externalPtrTag(&self) -> Self {
+        self.external_ptr_tag()
+    }
+
+    /// Internal function used to implement `#[extendr]` impl
+    #[doc(hidden)]
+    pub unsafe fn external_ptr_tag(&self) -> Self {
+        new_borrowed(R_ExternalPtrTag(self.get()))
+    }
+
+    /// Internal function used to implement `#[extendr]` impl
+    #[doc(hidden)]
+    pub unsafe fn external_ptr_protected(&self) -> Self {
+        new_borrowed(R_ExternalPtrProtected(self.get()))
+    }
+
+    #[doc(hidden)]
+    pub unsafe fn registerCFinalizer(&self, func: R_CFinalizer_t) {
+        self.register_c_finalizer(func)
+    }
+
+    #[doc(hidden)]
+    pub unsafe fn register_c_finalizer(&self, func: R_CFinalizer_t) {
+        R_RegisterCFinalizer(self.get(), func);
+    }
+
+    // SEXP R_ExternalPtrTag(SEXP s);
+    // SEXP R_ExternalPtrProtected(SEXP s);
+    // void R_ClearExternalPtr(SEXP s);
+    // void R_SetExternalPtrAddr(SEXP s, void *p);
+    // void R_SetExternalPtrTag(SEXP s, SEXP tag);
+    // void R_SetExternalPtrProtected(SEXP s, SEXP p);
+
+    /*
+    SEXP R_MakeWeakRef(SEXP key, SEXP val, SEXP fin, Rboolean onexit);
+    SEXP R_MakeWeakRefC(SEXP key, SEXP val, R_CFinalizer_t fin, Rboolean onexit);
+    SEXP R_WeakRefKey(SEXP w);
+    SEXP R_WeakRefValue(SEXP w);
+    void R_RunWeakRefFinalizer(SEXP w);
+    SEXP R_PromiseExpr(SEXP);
+    SEXP R_ClosureExpr(SEXP);
+    SEXP R_BytecodeExpr(SEXP e);
+    SEXP R_bcEncode(SEXP);
+    SEXP R_bcDecode(SEXP);
+    void R_registerBC(SEXP, SEXP);
+    Rboolean R_checkConstants(Rboolean);
+    Rboolean R_BCVersionOK(SEXP);
+    void R_RestoreHashCount(SEXP rho);
+    Rboolean R_IsPackageEnv(SEXP rho);
+    SEXP R_PackageEnvName(SEXP rho);
+    SEXP R_FindPackageEnv(SEXP info);
+    Rboolean R_IsNamespaceEnv(SEXP rho);
+    SEXP R_NamespaceEnvSpec(SEXP rho);
+    SEXP R_FindNamespace(SEXP info);
+    void R_LockEnvironment(SEXP env, Rboolean bindings);
+    Rboolean R_EnvironmentIsLocked(SEXP env);
+    void R_LockBinding(SEXP sym, SEXP env);
+    void R_unLockBinding(SEXP sym, SEXP env);
+    void R_MakeActiveBinding(SEXP sym, SEXP fun, SEXP env);
+    Rboolean R_BindingIsLocked(SEXP sym, SEXP env);
+    Rboolean R_BindingIsActive(SEXP sym, SEXP env);
+    Rboolean R_HasFancyBindings(SEXP rho);
+    */
+
+    /// Read-only access to attribute list.
+    // fn attrib(&self) -> Robj {
+    // unsafe {new_borrowed(ATTRIB(self.get()))}
+    // }
+
+    /// Copy a vector and resize it.
+    /// See. https://github.com/hadley/r-internals/blob/master/vectors.md
+    pub fn xlengthgets(&self, new_len: usize) -> Result<Robj, AnyError> {
+        unsafe {
+            if self.is_vector() {
+                Ok(new_owned(Rf_xlengthgets(self.get(), new_len as R_xlen_t)))
+            } else {
+                Err(AnyError::from("xlengthgets: Not a vector type"))
+            }
+        }
+    }
+
+    /// Allocated an owned object of a certain type.
+    pub fn alloc_vector(sexptype: u32, len: usize) -> Robj {
+        unsafe { new_owned(Rf_allocVector(sexptype, len as R_xlen_t)) }
+    }
+
+    /// Return true if two arrays have identical dims.
+    pub fn conformable(a: &Robj, b: &Robj) -> bool {
+        unsafe { Rf_conformable(a.get(), b.get()) != 0 }
+    }
+
+    /// Borrow an element from a list.
+    pub fn elt(&self, index: usize) -> Robj {
+        unsafe { Robj::from(Rf_elt(self.get(), index as raw::c_int)) }
+    }
+
+    //Rboolean Rf_inherits(SEXP, const char *);
+
+    /// Return true if this is an array.
+    pub fn is_array(&self) -> bool {
+        unsafe { Rf_isArray(self.get()) != 0 }
+    }
+
+    /// Return true if this is factor.
+    pub fn is_factor(&self) -> bool {
+        unsafe { Rf_isFactor(self.get()) != 0 }
+    }
+
+    /// Return true if this is a data frame.
+    pub fn is_frame(&self) -> bool {
+        unsafe { Rf_isFrame(self.get()) != 0 }
+    }
+
+    /// Return true if this is a function.
+    pub fn is_function(&self) -> bool {
+        unsafe { Rf_isFunction(self.get()) != 0 }
+    }
+
+    /// Return true if this is an integer vector.
+    pub fn is_integer(&self) -> bool {
+        unsafe { Rf_isInteger(self.get()) != 0 }
+    }
+
+    /// Return true if this is a language object.
+    pub fn is_language(&self) -> bool {
+        unsafe { Rf_isLanguage(self.get()) != 0 }
+    }
+
+    /// Return true if this is a vector list.
+    pub fn is_list(&self) -> bool {
+        unsafe { Rf_isList(self.get()) != 0 }
+    }
+
+    /// Return true if this is a matrix.
+    pub fn is_matrix(&self) -> bool {
+        unsafe { Rf_isMatrix(self.get()) != 0 }
+    }
+
+    /// Return true if this is a vector list or null.
+    pub fn is_new_list(&self) -> bool {
+        unsafe { Rf_isNewList(self.get()) != 0 }
+    }
+
+    /// Return true if this is a numeric vector but not a factor.
+    pub fn is_number(&self) -> bool {
+        unsafe { Rf_isNumber(self.get()) != 0 }
+    }
+
+    /// Return true if this is a pairlist.
+    pub fn is_pair_list(&self) -> bool {
+        unsafe { Rf_isPairList(self.get()) != 0 }
+    }
+
+    /// Return true if this is a primitive function.
+    pub fn is_primitive(&self) -> bool {
+        unsafe { Rf_isPrimitive(self.get()) != 0 }
+    }
+
+    /// Return true if this is a time series vector (see tsp).
+    pub fn is_ts(&self) -> bool {
+        unsafe { Rf_isTs(self.get()) != 0 }
+    }
+
+    /// Return true if this is a user defined binop.
+    pub fn is_user_binop(&self) -> bool {
+        unsafe { Rf_isUserBinop(self.get()) != 0 }
+    }
+
+    /// Return true if this is a valid string.
+    pub fn is_valid_string(&self) -> bool {
+        unsafe { Rf_isValidString(self.get()) != 0 }
+    }
+
+    /// Return true if this is a valid string.
+    pub fn is_valid_string_f(&self) -> bool {
+        unsafe { Rf_isValidStringF(self.get()) != 0 }
+    }
+
+    /// Return true if this is a vector.
+    pub fn is_vector(&self) -> bool {
+        unsafe { Rf_isVector(self.get()) != 0 }
+    }
+
+    /// Return true if this is an atomic vector.
+    pub fn is_vector_atomic(&self) -> bool {
+        unsafe { Rf_isVectorAtomic(self.get()) != 0 }
+    }
+
+    /// Return true if this is a vector list.
+    pub fn is_vector_list(&self) -> bool {
+        unsafe { Rf_isVectorList(self.get()) != 0 }
+    }
+
+    /// Return true if this is can be made into a vector.
+    pub fn is_vectorizable(&self) -> bool {
+        unsafe { Rf_isVectorizable(self.get()) != 0 }
+    }
+
+    /// Check an external pointer tag.
+    /// This is used to wrap R objects.
+    #[doc(hidden)]
+    pub fn check_external_ptr(&self, expected_tag: &str) -> bool {
+        if self.sexptype() == libR_sys::EXTPTRSXP {
+            let tag = unsafe { self.externalPtrTag() };
+            if tag.as_str() == Some(expected_tag) {
+                return true;
+            }
+        }
+        false
+    }
+}
+
+pub fn find_namespace(name: &str) -> Robj {
+    let name = r!(Symbol(name));
+    unsafe { new_borrowed(R_FindNamespace(name.get())) }
+}
diff --git a/extendr-api/src/robj/symbols.rs b/extendr-api/src/robj/symbols.rs
new file mode 100644
index 000000000..bc393c746
--- /dev/null
+++ b/extendr-api/src/robj/symbols.rs
@@ -0,0 +1,208 @@
+use super::*;
+
+/// The "global" environment
+pub fn global_env() -> Robj {
+    unsafe { new_sys(R_GlobalEnv) }
+}
+
+/// An empty environment at the root of the environment tree
+pub fn empty_env() -> Robj {
+    unsafe { new_sys(R_EmptyEnv) }
+}
+
+/// The base environment; formerly R_NilValue
+pub fn base_env() -> Robj {
+    unsafe { new_sys(R_BaseEnv) }
+}
+
+/// The (fake) namespace for base
+pub fn base_namespace() -> Robj {
+    unsafe { new_sys(R_BaseNamespace) }
+}
+
+/// for registered namespaces
+pub fn namespace_registry() -> Robj {
+    unsafe { new_sys(R_NamespaceRegistry) }
+}
+
+/// Current srcref, for debuggers
+pub fn srcref() -> Robj {
+    unsafe { new_sys(R_Srcref) }
+}
+
+/// The nil object
+pub fn nil_value() -> Robj {
+    unsafe { new_sys(R_NilValue) }
+}
+
+/// Unbound marker
+pub fn unbound_value() -> Robj {
+    unsafe { new_sys(R_UnboundValue) }
+}
+
+/// Missing argument marker
+pub fn missing_arg() -> Robj {
+    unsafe { new_sys(R_MissingArg) }
+}
+
+/// "base"
+pub fn base_symbol() -> Robj {
+    unsafe { new_sys(R_BaseSymbol) }
+}
+
+/// "{"
+pub fn brace_symbol() -> Robj {
+    unsafe { new_sys(R_BraceSymbol) }
+}
+
+/// "[["
+pub fn bracket_2_symbol() -> Robj {
+    unsafe { new_sys(R_Bracket2Symbol) }
+}
+
+/// "["
+pub fn bracket_symbol() -> Robj {
+    unsafe { new_sys(R_BracketSymbol) }
+}
+
+/// "class"
+pub fn class_symbol() -> Robj {
+    unsafe { new_sys(R_ClassSymbol) }
+}
+
+/// ".Device"
+pub fn device_symbol() -> Robj {
+    unsafe { new_sys(R_DeviceSymbol) }
+}
+
+/// "dimnames"
+pub fn dimnames_symbol() -> Robj {
+    unsafe { new_sys(R_DimNamesSymbol) }
+}
+
+/// "dim"
+pub fn dim_symbol() -> Robj {
+    unsafe { new_sys(R_DimSymbol) }
+}
+
+/// "$"
+pub fn dollar_symbol() -> Robj {
+    unsafe { new_sys(R_DollarSymbol) }
+}
+
+/// "..."
+pub fn dots_symbol() -> Robj {
+    unsafe { new_sys(R_DotsSymbol) }
+}
+//     pub fn drop_symbol() -> Robj { unsafe { new_sys(R_DropSymbol) }}"drop"
+
+/// "::"
+pub fn double_colon_symbol() -> Robj {
+    unsafe { new_sys(R_DoubleColonSymbol) }
+}
+
+/// ".Last.value"
+pub fn lastvalue_symbol() -> Robj {
+    unsafe { new_sys(R_LastvalueSymbol) }
+}
+/// "levels"
+pub fn levels_symbol() -> Robj {
+    unsafe { new_sys(R_LevelsSymbol) }
+}
+/// "mode"
+pub fn mode_symbol() -> Robj {
+    unsafe { new_sys(R_ModeSymbol) }
+}
+/// "na.rm"
+pub fn na_rm_symbol() -> Robj {
+    unsafe { new_sys(R_NaRmSymbol) }
+}
+/// "name"
+pub fn name_symbol() -> Robj {
+    unsafe { new_sys(R_NameSymbol) }
+}
+/// "names"
+pub fn names_symbol() -> Robj {
+    unsafe { new_sys(R_NamesSymbol) }
+}
+/// _NAMESPACE__."
+pub fn namespace_env_symbol() -> Robj {
+    unsafe { new_sys(R_NamespaceEnvSymbol) }
+}
+/// "package"
+pub fn package_symbol() -> Robj {
+    unsafe { new_sys(R_PackageSymbol) }
+}
+/// "previous"
+pub fn previous_symbol() -> Robj {
+    unsafe { new_sys(R_PreviousSymbol) }
+}
+/// "quote"
+pub fn quote_symbol() -> Robj {
+    unsafe { new_sys(R_QuoteSymbol) }
+}
+/// "row.names"
+pub fn row_names_symbol() -> Robj {
+    unsafe { new_sys(R_RowNamesSymbol) }
+}
+/// ".Random.seed"
+pub fn seeds_symbol() -> Robj {
+    unsafe { new_sys(R_SeedsSymbol) }
+}
+/// "sort.list"
+pub fn sort_list_symbol() -> Robj {
+    unsafe { new_sys(R_SortListSymbol) }
+}
+/// "source"
+pub fn source_symbol() -> Robj {
+    unsafe { new_sys(R_SourceSymbol) }
+}
+/// "spec"
+pub fn spec_symbol() -> Robj {
+    unsafe { new_sys(R_SpecSymbol) }
+}
+/// "tsp"
+pub fn tsp_symbol() -> Robj {
+    unsafe { new_sys(R_TspSymbol) }
+}
+/// ":::"
+pub fn triple_colon_symbol() -> Robj {
+    unsafe { new_sys(R_TripleColonSymbol) }
+}
+/// ".defined"
+pub fn dot_defined() -> Robj {
+    unsafe { new_sys(R_dot_defined) }
+}
+/// ".Method"
+pub fn dot_method() -> Robj {
+    unsafe { new_sys(R_dot_Method) }
+}
+/// "packageName"
+pub fn dot_package_name() -> Robj {
+    unsafe { new_sys(R_dot_packageName) }
+}
+
+/// ".target"
+pub fn dot_target() -> Robj {
+    unsafe { new_sys(R_dot_target) }
+}
+
+/* fix version issues.
+/// ".Generic"
+pub fn dot_Generic() -> Robj { unsafe { new_sys(R_dot_Generic) }}
+*/
+
+/// NA_STRING as a CHARSXP
+pub fn na_string() -> Robj {
+    unsafe { new_sys(R_NaString) }
+}
+
+/// "" as a CHARSXP
+pub fn blank_string() -> Robj {
+    unsafe { new_sys(R_BlankString) }
+}
+
+/// "" as a STRSXP
+pub fn blank_scalar_string() -> Robj {
+    unsafe { new_sys(R_BlankScalarString) }
+}
diff --git a/extendr-api/src/robj_ndarray.rs b/extendr-api/src/robj_ndarray.rs
index 257dc4738..acdd43eec 100644
--- a/extendr-api/src/robj_ndarray.rs
+++ b/extendr-api/src/robj_ndarray.rs
@@ -1,4 +1,6 @@
+#[doc(hidden)]
 use libR_sys::*;
+#[doc(hidden)]
 use ndarray::prelude::*;
 
 use crate::logical::Bool;
diff --git a/extendr-api/src/wrapper.rs b/extendr-api/src/wrapper.rs
index 946bf4414..9c378f034 100644
--- a/extendr-api/src/wrapper.rs
+++ b/extendr-api/src/wrapper.rs
@@ -1,24 +1,65 @@
 //! Wrappers are lightweight proxies for references to R datatypes.
 //! They do not contain an Robj (see array.rs for an example of this).
 
-use crate::logical::*;
 use crate::robj::*;
+#[doc(hidden)]
 use libR_sys::*;
+#[doc(hidden)]
 use std::ffi::CString;
 
 /// Wrapper for creating symbols.
+///
+/// Example:
+/// ```
+/// use extendr_api::*;
+/// extendr_engine::start_r();
+/// let symbol = r!(Symbol("xyz"));
+/// assert_eq!(symbol.as_str(), Some("xyz"));
+/// assert!(symbol.is_symbol());
+/// ```
+/// Note that creating a symbol from a string is expensive
+/// and so you may want to cache them.
+///
 #[derive(Debug, PartialEq)]
 pub struct Symbol<'a>(pub &'a str);
 
 /// Wrapper for creating character objects.
+/// These are used only as the contents of a character
+/// vector.
+///
+/// Example:
+/// ```
+/// use extendr_api::*;
+/// extendr_engine::start_r();
+/// let chr = r!(Character("xyz"));
+/// assert_eq!(chr.as_str(), Some("xyz"));
+/// ```
+///
 #[derive(Debug, PartialEq)]
 pub struct Character<'a>(pub &'a str);
 
 /// Wrapper for creating language objects.
+/// Example:
+/// ```
+/// use extendr_api::*;
+/// extendr_engine::start_r();
+/// let _call_to_xyz = r!(Lang("xyz"));
+/// ```
+///
+/// Note: prefer to use the [lang!] macro for this.
 #[derive(Debug, PartialEq)]
 pub struct Lang<'a>(pub &'a str);
 
 /// Wrapper for creating list objects.
+/// Example:
+/// ```
+/// use extendr_api::*;
+/// extendr_engine::start_r();
+/// let mixed_list = r!(List(&[r!(1.), r!("xyz")]));
+/// assert_eq!(mixed_list.len(), 2);
+/// ```
+///
+/// Note: prefer to use the [list!] macro for this.
 #[derive(Debug, PartialEq)]
 pub struct List<'a>(pub &'a [Robj]);
 
@@ -38,8 +79,8 @@ impl<'a> PartialEq<List<'a>> for Robj {
     }
 }
 
-/// Make a list object from an array of Robjs.
 impl<'a> From<List<'a>> for Robj {
+    /// Make a list object from an array of Robjs.
     fn from(val: List<'a>) -> Self {
         unsafe {
             let sexp = Rf_allocVector(VECSXP, val.0.len() as R_xlen_t);
@@ -52,25 +93,8 @@ impl<'a> From<List<'a>> for Robj {
     }
 }
 
-/// Convert an integer slice to a logical object.
-impl<'a> From<&'a [Bool]> for Robj {
-    fn from(vals: &[Bool]) -> Self {
-        unsafe {
-            let len = vals.len();
-            let sexp = Rf_allocVector(LGLSXP, len as R_xlen_t);
-            R_PreserveObject(sexp);
-            let ptr = LOGICAL(sexp);
-            let slice = std::slice::from_raw_parts_mut(ptr, len);
-            for (i, &v) in vals.iter().enumerate() {
-                slice[i] = v.0;
-            }
-            Robj::Owned(sexp)
-        }
-    }
-}
-
-/// Convert a string to a symbol.
 impl<'a> From<Symbol<'a>> for Robj {
+    /// Convert a string to a symbol.
     fn from(name: Symbol) -> Self {
         unsafe {
             if let Ok(name) = CString::new(name.0) {
@@ -81,3 +105,13 @@ impl<'a> From<Symbol<'a>> for Robj {
         }
     }
 }
+
+impl<'a> From<Lang<'a>> for Robj {
+    /// Convert a wrapped string ref to an Robj language object.
+    fn from(val: Lang<'a>) -> Self {
+        unsafe {
+            let name = Robj::from(Symbol(val.0));
+            new_owned(Rf_lang1(name.get()))
+        }
+    }
+}


Test patch:
diff --git a/extendr-api/src/robj/tests.rs b/extendr-api/src/robj/tests.rs
new file mode 100644
index 000000000..27201c708
--- /dev/null
+++ b/extendr-api/src/robj/tests.rs
@@ -0,0 +1,302 @@
+use super::*;
+
+#[test]
+fn test_debug() {
+    extendr_engine::start_r();
+    // Special values
+    assert_eq!(format!("{:?}", Robj::from(NULL)), "NULL");
+    assert_eq!(format!("{:?}", Robj::from(TRUE)), "TRUE");
+    assert_eq!(format!("{:?}", Robj::from(FALSE)), "FALSE");
+
+    // Scalars
+    assert_eq!(format!("{:?}", Robj::from(1)), "1");
+    assert_eq!(format!("{:?}", Robj::from(1.)), "1.0");
+    assert_eq!(format!("{:?}", Robj::from("hello")), "[\"hello\"]");
+    let s = "hello".to_string();
+    assert_eq!(format!("{:?}", Robj::from(s)), "[\"hello\"]");
+
+    // Vectors
+    assert_eq!(format!("{:?}", Robj::from([1, 2, 3])), "[1, 2, 3]");
+    assert_eq!(format!("{:?}", Robj::from([1., 2., 3.])), "[1.0, 2.0, 3.0]");
+    assert_eq!(format!("{:?}", Robj::from([1_u8, 2_u8, 3_u8])), "[1, 2, 3]");
+
+    // Wrappers
+    assert_eq!(format!("{:?}", Robj::from(Symbol("x"))), "Symbol(\"x\")");
+    assert_eq!(
+        format!("{:?}", Robj::from(Character("x"))),
+        "Character(\"x\")"
+    );
+    assert_eq!(
+        format!("{:?}", Robj::from(Lang("x"))),
+        "Lang([Symbol(\"x\")])"
+    );
+
+    // Logical
+    assert_eq!(
+        format!("{:?}", Robj::from([Bool(1), Bool(0)])),
+        "&[Bool(1), Bool(0)]"
+    );
+}
+
+#[test]
+fn test_from_robj() {
+    extendr_engine::start_r();
+    assert_eq!(<bool>::from_robj(&Robj::from(true)), Ok(true));
+    assert_eq!(<u8>::from_robj(&Robj::from(1)), Ok(1));
+    assert_eq!(<u16>::from_robj(&Robj::from(1)), Ok(1));
+    assert_eq!(<u32>::from_robj(&Robj::from(1)), Ok(1));
+    assert_eq!(<u64>::from_robj(&Robj::from(1)), Ok(1));
+    assert_eq!(<i8>::from_robj(&Robj::from(1)), Ok(1));
+    assert_eq!(<i16>::from_robj(&Robj::from(1)), Ok(1));
+    assert_eq!(<i32>::from_robj(&Robj::from(1)), Ok(1));
+    assert_eq!(<i64>::from_robj(&Robj::from(1)), Ok(1));
+    assert_eq!(<f32>::from_robj(&Robj::from(1)), Ok(1.));
+    assert_eq!(<f64>::from_robj(&Robj::from(1)), Ok(1.));
+
+    assert_eq!(<Vec::<i32>>::from_robj(&Robj::from(1)), Ok(vec![1]));
+    assert_eq!(<Vec::<f64>>::from_robj(&Robj::from(1.)), Ok(vec![1.]));
+
+    /*
+    assert_eq!(
+        <ArrayView1<f64>>::from_robj(&Robj::from(1.)),
+        Ok(ArrayView1::<f64>::from(&[1.][..]))
+    );
+    assert_eq!(
+        <ArrayView1<i32>>::from_robj(&Robj::from(1)),
+        Ok(ArrayView1::<i32>::from(&[1][..]))
+    );
+    assert_eq!(
+        <ArrayView1<Bool>>::from_robj(&Robj::from(true)),
+        Ok(ArrayView1::<Bool>::from(&[Bool(1)][..]))
+    );
+    assert_eq!(
+        <ArrayView2<f64>>::from_robj(&Robj::from(1.)),
+        Ok(ArrayView2::<f64>::from_shape((1, 1), &[1.][..]).unwrap())
+    );
+    assert_eq!(
+        <ArrayView2<i32>>::from_robj(&Robj::from(1)),
+        Ok(ArrayView2::<i32>::from_shape((1, 1), &[1][..]).unwrap())
+    );
+    assert_eq!(
+        <ArrayView2<Bool>>::from_robj(&Robj::from(true)),
+        Ok(ArrayView2::<Bool>::from_shape((1, 1), &[Bool(1)][..]).unwrap())
+    );
+
+    assert_eq!(
+        <ArrayView2<f64>>::from_robj(
+            &Robj::eval_string("matrix(c(1, 2, 3, 4, 5, 6, 7, 8), ncol = 2, nrow = 4, byrow = T)")
+                .unwrap()
+        ),
+        Ok(ArrayView2::<f64>::from_shape(
+            (4, 2),
+            &[1f64, 2f64, 3f64, 4f64, 5f64, 6f64, 7f64, 8f64][..]
+        )
+        .unwrap())
+    );
+    */
+
+    let hello = Robj::from("hello");
+    assert_eq!(<&str>::from_robj(&hello), Ok("hello"));
+
+    // conversion from a vector to a scalar value
+    assert_eq!(
+        <i32>::from_robj(&Robj::from(vec![].as_slice() as &[i32])),
+        Err("Input must be of length 1. Vector of length zero given.")
+    );
+    assert_eq!(
+        <i32>::from_robj(&Robj::from(vec![1].as_slice() as &[i32])),
+        Ok(1)
+    );
+    assert_eq!(
+        <i32>::from_robj(&Robj::from(vec![1, 2].as_slice() as &[i32])),
+        Err("Input must be of length 1. Vector of length >1 given.")
+    );
+
+    use std::collections::HashMap;
+    let list = Robj::eval_string("list(a = 1L, b = 2L)").unwrap();
+    let hmap1 = [("a".into(), 1.into()), ("b".into(), 2.into())]
+        .iter()
+        .cloned()
+        .collect::<HashMap<String, Robj>>();
+    let hmap2 = [("a", 1.into()), ("b", 2.into())]
+        .iter()
+        .cloned()
+        .collect::<HashMap<&str, Robj>>();
+    let hmap_owned = <HashMap<String, Robj>>::from_robj(&list).unwrap();
+    let hmap_borrowed = <HashMap<&str, Robj>>::from_robj(&list).unwrap();
+    assert_eq!(hmap_owned, hmap1);
+    assert_eq!(hmap_borrowed, hmap2);
+
+    assert!(hmap_owned["a"].is_owned());
+    assert_eq!(hmap_owned["a"], Robj::from(1));
+    assert_eq!(hmap_owned["b"], Robj::from(2));
+
+    assert_eq!(hmap_borrowed["a"], Robj::from(1));
+    assert_eq!(hmap_borrowed["b"], Robj::from(2));
+
+    let na_integer = Robj::eval_string("NA_integer_").unwrap();
+    assert!(<i32>::from_robj(&na_integer).is_err());
+    assert_eq!(<Option<i32>>::from_robj(&na_integer), Ok(None));
+    assert_eq!(<Option<i32>>::from_robj(&Robj::from(1)), Ok(Some(1)));
+    assert!(<Option<i32>>::from_robj(&Robj::from([1, 2])).is_err());
+
+    let na_bool = Robj::eval_string("TRUE == NA").unwrap();
+    assert!(<bool>::from_robj(&na_bool).is_err());
+    assert_eq!(<Option<bool>>::from_robj(&na_bool), Ok(None));
+    assert_eq!(<Option<bool>>::from_robj(&Robj::from(true)), Ok(Some(true)));
+    assert!(<Option<bool>>::from_robj(&Robj::from([true, false])).is_err());
+
+    let na_real = Robj::eval_string("NA_real_").unwrap();
+    assert!(<f64>::from_robj(&na_real).is_err());
+    assert_eq!(<Option<f64>>::from_robj(&na_real), Ok(None));
+    assert_eq!(<Option<f64>>::from_robj(&Robj::from(1.)), Ok(Some(1.)));
+    assert!(<Option<f64>>::from_robj(&Robj::from([1., 2.])).is_err());
+
+    let na_string = Robj::eval_string("NA_character_").unwrap();
+    assert!(<&str>::from_robj(&na_string).is_err());
+    assert_eq!(<Option<&str>>::from_robj(&na_string), Ok(None));
+    assert_eq!(<Option<&str>>::from_robj(&Robj::from("1")), Ok(Some("1")));
+    assert!(<Option<&str>>::from_robj(&Robj::from(["1", "2"])).is_err());
+
+    let na_string = Robj::eval_string("NA_character_").unwrap();
+    assert!(<String>::from_robj(&na_string).is_err());
+    assert_eq!(<Option<String>>::from_robj(&na_string), Ok(None));
+    assert_eq!(
+        <Option<String>>::from_robj(&Robj::from("1")),
+        Ok(Some("1".to_string()))
+    );
+    assert!(<Option<String>>::from_robj(&Robj::from(["1", "2"])).is_err());
+}
+#[test]
+fn test_to_robj() {
+    extendr_engine::start_r();
+    assert_eq!(Robj::from(true), Robj::from([Bool::from(true)]));
+    //assert_eq!(Robj::from(1_u8), Robj::from(1));
+    assert_eq!(Robj::from(1_u16), Robj::from(1));
+    assert_eq!(Robj::from(1_u32), Robj::from(1));
+    assert_eq!(Robj::from(1_u64), Robj::from(1));
+    assert_eq!(Robj::from(1_i8), Robj::from(1));
+    assert_eq!(Robj::from(1_i16), Robj::from(1));
+    assert_eq!(Robj::from(1_i32), Robj::from(1));
+    assert_eq!(Robj::from(1_i64), Robj::from(1));
+    assert_eq!(Robj::from(1.0_f32), Robj::from(1.));
+    assert_eq!(Robj::from(1.0_f64), Robj::from(1.));
+
+    let ab = Robj::from(vec!["a", "b"]);
+    let ab2 = Robj::from(vec!["a".to_string(), "b".to_string()]);
+    assert_eq!(ab, ab2);
+    assert_eq!(format!("{:?}", ab), "[\"a\", \"b\"]");
+    assert_eq!(format!("{:?}", ab2), "[\"a\", \"b\"]");
+
+    assert_eq!(Robj::from(Some(1)), Robj::from(1));
+    assert!(!Robj::from(Some(1)).is_na());
+    assert!(Robj::from(<Option<i32>>::None).is_na());
+
+    assert_eq!(Robj::from(Some(true)), Robj::from(true));
+    assert!(!Robj::from(Some(true)).is_na());
+    assert!(Robj::from(<Option<bool>>::None).is_na());
+
+    assert_eq!(Robj::from(Some(1.)), Robj::from(1.));
+    assert!(!Robj::from(Some(1.)).is_na());
+    assert!(Robj::from(<Option<f64>>::None).is_na());
+
+    assert_eq!(Robj::from(Some("xyz")), Robj::from("xyz"));
+    assert!(!Robj::from(Some("xyz")).is_na());
+    assert!(Robj::from(<Option<&str>>::None).is_na());
+}
+
+#[test]
+fn parse_test() -> Result<(), AnyError> {
+    extendr_engine::start_r();
+    let p = Robj::parse("print(1L);print(1L);")?;
+    assert_eq!(
+        format!("{:?}", p),
+        "Expr([Lang([Symbol(\"print\"), 1]), Lang([Symbol(\"print\"), 1])])"
+    );
+
+    let p = Robj::eval_string("1L + 1L")?;
+    assert_eq!(p, Robj::from(2));
+    Ok(())
+}
+
+#[test]
+fn output_iterator_test() -> Result<(), AnyError> {
+    extendr_engine::start_r();
+
+    // Allocation where size is known in advance.
+    let robj = (0..3).collect_robj();
+    assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 1, 2]);
+
+    let robj = [0, 1, 2].iter().collect_robj();
+    assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 1, 2]);
+
+    let robj = (0..3).map(|x| x % 2 == 0).collect_robj();
+    assert_eq!(robj.as_logical_vector().unwrap(), vec![TRUE, FALSE, TRUE]);
+
+    let robj = [TRUE, FALSE, TRUE].iter().collect_robj();
+    assert_eq!(robj.as_logical_vector().unwrap(), vec![TRUE, FALSE, TRUE]);
+
+    let robj = (0..3).map(|x| x as f64).collect_robj();
+    assert_eq!(robj.as_real_vector().unwrap(), vec![0., 1., 2.]);
+
+    let robj = [0., 1., 2.].iter().collect_robj();
+    assert_eq!(robj.as_real_vector().unwrap(), vec![0., 1., 2.]);
+
+    let robj = (0..3).map(|x| format!("{}", x)).collect_robj();
+    assert_eq!(robj.as_str_vector(), Some(vec!["0", "1", "2"]));
+
+    let robj = ["0", "1", "2"].iter().collect_robj();
+    assert_eq!(robj.as_str_vector(), Some(vec!["0", "1", "2"]));
+
+    // Fallback allocation where size is not known in advance.
+    let robj = (0..3).filter(|&x| x != 1).collect_robj();
+    assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 2]);
+
+    let robj = (0..3).filter(|&x| x != 1).map(|x| x as f64).collect_robj();
+    assert_eq!(robj.as_real_vector().unwrap(), vec![0., 2.]);
+
+    let robj = (0..3)
+        .filter(|&x| x != 1)
+        .map(|x| format!("{}", x))
+        .collect_robj();
+    assert_eq!(robj.as_str_vector(), Some(vec!["0", "2"]));
+
+    Ok(())
+}
+
+// Test that we can use Iterators as the input to functions.
+// eg.
+// #[extendr]
+// fn fred(a: RealIter, b: RealIter) -> RealIter {
+// }
+#[test]
+fn input_iterator_test() {
+    extendr_engine::start_r();
+
+    let src: &[&str] = &["1", "2", "3"];
+    let robj = Robj::from(src);
+    let iter = <StrIter>::from_robj(&robj).unwrap();
+    assert_eq!(iter.collect::<Vec<_>>(), src);
+
+    let src = &[Robj::from(1), Robj::from(2), Robj::from(3)];
+    let robj = Robj::from(List(src));
+    let iter = <VecIter>::from_robj(&robj).unwrap();
+    assert_eq!(iter.collect::<Vec<_>>(), src);
+
+    let src: &[i32] = &[1, 2, 3];
+    let robj = Robj::from(src);
+    let iter = <IntegerIter>::from_robj(&robj).unwrap();
+    assert_eq!(iter.cloned().collect::<Vec<_>>(), src);
+
+    let src: &[f64] = &[1., 2., 3.];
+    let robj = Robj::from(src);
+    let iter = <RealIter>::from_robj(&robj).unwrap();
+    assert_eq!(iter.cloned().collect::<Vec<_>>(), src);
+
+    /*
+    let src: &[Bool] = &[TRUE, FALSE, TRUE];
+    let robj = Robj::from(src);
+    let iter = <LogicalIter>::from_robj(&robj).unwrap();
+    assert_eq!(iter.cloned().collect::<Vec<_>>(), src);
+    */
+}