Repository: extendr/extendr (rust)

Issue:
Task: Compile a list of TryFrom conversions and test against it.
For example.

On Input:

Let B bet the set of basic types:

```
i8/u8-i128/u128 isize/usize
f32/f64
Rcomplex (from libR-sys).
```

Let S be the set of scalar types:

`Rint, Rfloat, Rint, Rcplx.`

Let W be the set of wrapper types including Robj.

`Robj, List, Integers etc.`

Let A be the set of slices:

`&[B], &[S], &[Rstr], &[Robj]`

Let O be the set of Options:

`Option<B>, Option<S>, Option<W>, Option<A>` and all other types.

We should have special behaviour for:

`&S, &W, &Rstr, &str - these can use TryFrom<&Robj>` and avoid cloning the parameter.


Option should always be None for NULL.

For NA, the set S has explicit methods.


In the code generator:

Primitive types B, references and Option<&X> should all use `TryFrom<&Robj>`.

We should encourage users to use references as these can be faster. Internally `&Robj` and `&SEXP`
are isomorphic.

We need code examples in rextendr for all of these.

The crate documentation needs to be updated.


Pull request:
Improved TryFrom implementations
This PR improves the TryFrom conversions used in input parameters.

* All `TryFrom` conversions now support `&Robj`
* All `TryFrom` conversions now support Option<> with NA and NULL filtering. 

Types supported:
```
    u8 u16 u32 u64 usize
    i8 i16 i32 i64 isize
    bool
    Rint Rfloat Rbool Rcplx
    f32 f64
    Vec::<Rint> Vec::<Rfloat> Vec::<Rbool> Vec::<Rcplx> Vec::<u8> Vec::<i32> Vec::<f64>
    &[Rint] &[Rfloat] &[Rbool] &[Rcplx] &[u8] &[i32] &[f64]
    &str String
```

closes #369 



Patch:
diff --git a/extendr-api/src/robj/try_from_robj.rs b/extendr-api/src/robj/try_from_robj.rs
index 23bd53f62..7b4712231 100644
--- a/extendr-api/src/robj/try_from_robj.rs
+++ b/extendr-api/src/robj/try_from_robj.rs
@@ -4,21 +4,21 @@ use super::*;
 
 macro_rules! impl_try_from_scalar_integer {
     ($t:ty) => {
-        impl TryFrom<Robj> for $t {
+        impl TryFrom<&Robj> for $t {
             type Error = Error;
 
             /// Convert a numeric object to an integer value.
-            fn try_from(robj: Robj) -> Result<Self> {
+            fn try_from(robj: &Robj) -> Result<Self> {
                 // Check if the value is a scalar
                 match robj.len() {
-                    0 => return Err(Error::ExpectedNonZeroLength(robj)),
+                    0 => return Err(Error::ExpectedNonZeroLength(robj.clone())),
                     1 => {}
-                    _ => return Err(Error::ExpectedScalar(robj)),
+                    _ => return Err(Error::ExpectedScalar(robj.clone())),
                 };
 
                 // Check if the value is not a missing value
                 if robj.is_na() {
-                    return Err(Error::MustNotBeNA(robj));
+                    return Err(Error::MustNotBeNA(robj.clone()));
                 }
 
                 // If the conversion is int-to-int, check the limits. This
@@ -29,7 +29,7 @@ macro_rules! impl_try_from_scalar_integer {
                     if let Ok(v) = Self::try_from(v) {
                         return Ok(v);
                     } else {
-                        return Err(Error::OutOfLimits(robj));
+                        return Err(Error::OutOfLimits(robj.clone()));
                     }
                 }
 
@@ -43,11 +43,11 @@ macro_rules! impl_try_from_scalar_integer {
                     if (result as f64 - v).abs() < f64::EPSILON {
                         return Ok(result);
                     } else {
-                        return Err(Error::ExpectedWholeNumber(robj));
+                        return Err(Error::ExpectedWholeNumber(robj.clone()));
                     }
                 }
 
-                Err(Error::ExpectedNumeric(robj))
+                Err(Error::ExpectedNumeric(robj.clone()))
             }
         }
     };
@@ -55,21 +55,21 @@ macro_rules! impl_try_from_scalar_integer {
 
 macro_rules! impl_try_from_scalar_real {
     ($t:ty) => {
-        impl TryFrom<Robj> for $t {
+        impl TryFrom<&Robj> for $t {
             type Error = Error;
 
             /// Convert a numeric object to a real value.
-            fn try_from(robj: Robj) -> Result<Self> {
+            fn try_from(robj: &Robj) -> Result<Self> {
                 // Check if the value is a scalar
                 match robj.len() {
-                    0 => return Err(Error::ExpectedNonZeroLength(robj)),
+                    0 => return Err(Error::ExpectedNonZeroLength(robj.clone())),
                     1 => {}
-                    _ => return Err(Error::ExpectedScalar(robj)),
+                    _ => return Err(Error::ExpectedScalar(robj.clone())),
                 };
 
                 // Check if the value is not a missing value
                 if robj.is_na() {
-                    return Err(Error::MustNotBeNA(robj));
+                    return Err(Error::MustNotBeNA(robj.clone()));
                 }
 
                 // <Robj>::as_xxx() methods can work only when the underlying
@@ -82,7 +82,7 @@ macro_rules! impl_try_from_scalar_real {
                     return Ok(v as Self);
                 }
 
-                Err(Error::ExpectedNumeric(robj))
+                Err(Error::ExpectedNumeric(robj.clone()))
             }
         }
     };
@@ -92,228 +92,341 @@ impl_try_from_scalar_integer!(u8);
 impl_try_from_scalar_integer!(u16);
 impl_try_from_scalar_integer!(u32);
 impl_try_from_scalar_integer!(u64);
+impl_try_from_scalar_integer!(usize);
 impl_try_from_scalar_integer!(i8);
 impl_try_from_scalar_integer!(i16);
 impl_try_from_scalar_integer!(i32);
 impl_try_from_scalar_integer!(i64);
+impl_try_from_scalar_integer!(isize);
 impl_try_from_scalar_real!(f32);
 impl_try_from_scalar_real!(f64);
 
-impl TryFrom<Robj> for Rbool {
-    type Error = Error;
-
-    /// Convert an LGLSXP object into a Rbool (tri-state boolean).
-    /// Use `value.is_na()` to detect NA values.
-    fn try_from(robj: Robj) -> Result<Self> {
-        if let Some(v) = robj.as_logical_slice() {
-            match v.len() {
-                0 => Err(Error::ExpectedNonZeroLength(robj)),
-                1 => Ok(v[0]),
-                _ => Err(Error::ExpectedScalar(robj)),
-            }
-        } else {
-            Err(Error::ExpectedLogical(robj))
-        }
-    }
-}
-
-impl TryFrom<Robj> for bool {
+impl TryFrom<&Robj> for bool {
     type Error = Error;
 
     /// Convert an LGLSXP object into a boolean.
     /// NAs are not allowed.
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         if robj.is_na() {
-            Err(Error::MustNotBeNA(robj))
+            Err(Error::MustNotBeNA(robj.clone()))
         } else {
             Ok(<Rbool>::try_from(robj)?.is_true())
         }
     }
 }
 
-impl TryFrom<Robj> for &str {
+impl TryFrom<&Robj> for &str {
     type Error = Error;
 
     /// Convert a scalar STRSXP object into a string slice.
     /// NAs are not allowed.
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         if robj.is_na() {
-            return Err(Error::MustNotBeNA(robj));
+            return Err(Error::MustNotBeNA(robj.clone()));
         }
         match robj.len() {
-            0 => Err(Error::ExpectedNonZeroLength(robj)),
+            0 => Err(Error::ExpectedNonZeroLength(robj.clone())),
             1 => {
                 if let Some(s) = robj.as_str() {
                     Ok(s)
                 } else {
-                    Err(Error::ExpectedString(robj))
+                    Err(Error::ExpectedString(robj.clone()))
                 }
             }
-            _ => Err(Error::ExpectedScalar(robj)),
+            _ => Err(Error::ExpectedScalar(robj.clone())),
         }
     }
 }
 
-impl TryFrom<Robj> for String {
+impl TryFrom<&Robj> for String {
     type Error = Error;
 
     /// Convert an scalar STRSXP object into a String.
     /// Note: Unless you plan to store the result, use a string slice instead.
     /// NAs are not allowed.
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         <&str>::try_from(robj).map(|s| s.to_string())
     }
 }
 
-impl TryFrom<Robj> for Vec<i32> {
+impl TryFrom<&Robj> for Vec<i32> {
     type Error = Error;
 
     /// Convert an INTSXP object into a vector of i32 (integer).
     /// Note: Unless you plan to store the result, use a slice instead.
     /// Use `value.is_na()` to detect NA values.
-    fn try_from(robj: Robj) -> Result<Self> {
-        if let Some(v) = robj.as_integer_slice() {
+    fn try_from(robj: &Robj) -> Result<Self> {
+        if let Some(v) = robj.as_typed_slice() {
+            // TODO: check NAs
             Ok(Vec::from(v))
         } else {
-            Err(Error::ExpectedInteger(robj))
+            Err(Error::ExpectedInteger(robj.clone()))
         }
     }
 }
 
-impl TryFrom<Robj> for Vec<f64> {
+impl TryFrom<&Robj> for Vec<f64> {
     type Error = Error;
 
     /// Convert a REALSXP object into a vector of f64 (double precision floating point).
     /// Note: Unless you plan to store the result, use a slice instead.
     /// Use `value.is_na()` to detect NA values.
-    fn try_from(robj: Robj) -> Result<Self> {
-        if let Some(v) = robj.as_real_slice() {
+    fn try_from(robj: &Robj) -> Result<Self> {
+        if let Some(v) = robj.as_typed_slice() {
+            // TODO: check NAs
             Ok(Vec::from(v))
         } else {
-            Err(Error::ExpectedReal(robj))
+            Err(Error::ExpectedReal(robj.clone()))
         }
     }
 }
 
-impl TryFrom<Robj> for Vec<Rbool> {
+impl TryFrom<&Robj> for Vec<u8> {
     type Error = Error;
 
-    /// Convert a LGLSXP object into a vector of Rbool (tri-state booleans).
+    /// Convert a RAWSXP object into a vector of bytes.
+    /// Note: Unless you plan to store the result, use a slice instead.
+    fn try_from(robj: &Robj) -> Result<Self> {
+        if let Some(v) = robj.as_typed_slice() {
+            Ok(Vec::from(v))
+        } else {
+            Err(Error::ExpectedRaw(robj.clone()))
+        }
+    }
+}
+
+impl TryFrom<&Robj> for Vec<Rint> {
+    type Error = Error;
+
+    /// Convert an INTSXP object into a vector of i32 (integer).
     /// Note: Unless you plan to store the result, use a slice instead.
     /// Use `value.is_na()` to detect NA values.
-    fn try_from(robj: Robj) -> Result<Self> {
-        if let Some(v) = robj.as_logical_slice() {
+    fn try_from(robj: &Robj) -> Result<Self> {
+        if let Some(v) = robj.as_typed_slice() {
             Ok(Vec::from(v))
         } else {
-            Err(Error::ExpectedInteger(robj))
+            Err(Error::ExpectedInteger(robj.clone()))
         }
     }
 }
 
-impl TryFrom<Robj> for Vec<u8> {
+impl TryFrom<&Robj> for Vec<Rfloat> {
     type Error = Error;
 
-    /// Convert a RAWSXP object into a vector of bytes.
+    /// Convert a REALSXP object into a vector of f64 (double precision floating point).
+    /// Note: Unless you plan to store the result, use a slice instead.
+    /// Use `value.is_na()` to detect NA values.
+    fn try_from(robj: &Robj) -> Result<Self> {
+        if let Some(v) = robj.as_typed_slice() {
+            Ok(Vec::from(v))
+        } else {
+            Err(Error::ExpectedReal(robj.clone()))
+        }
+    }
+}
+
+impl TryFrom<&Robj> for Vec<Rbool> {
+    type Error = Error;
+
+    /// Convert a LGLSXP object into a vector of Rbool (tri-state booleans).
     /// Note: Unless you plan to store the result, use a slice instead.
-    fn try_from(robj: Robj) -> Result<Self> {
-        if let Some(v) = robj.as_raw_slice() {
+    /// Use `value.is_na()` to detect NA values.
+    fn try_from(robj: &Robj) -> Result<Self> {
+        if let Some(v) = robj.as_typed_slice() {
             Ok(Vec::from(v))
         } else {
-            Err(Error::ExpectedRaw(robj))
+            Err(Error::ExpectedInteger(robj.clone()))
         }
     }
 }
 
-impl TryFrom<Robj> for Vec<String> {
+impl TryFrom<&Robj> for Vec<Rcplx> {
+    type Error = Error;
+
+    /// Convert a complex object into a vector of Rcplx.
+    fn try_from(robj: &Robj) -> Result<Self> {
+        if let Some(v) = robj.as_typed_slice() {
+            Ok(Vec::from(v))
+        } else {
+            Err(Error::ExpectedComplex(robj.clone()))
+        }
+    }
+}
+
+impl TryFrom<&Robj> for Vec<String> {
     type Error = Error;
 
     /// Convert a STRSXP object into a vector of `String`s.
     /// Note: Unless you plan to store the result, use a slice instead.
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         if let Some(iter) = robj.as_str_iter() {
             // check for NA's in the string vector
             if iter.clone().any(|s| s.is_na()) {
-                Err(Error::MustNotBeNA(robj))
+                Err(Error::MustNotBeNA(robj.clone()))
             } else {
                 Ok(iter.map(|s| s.to_string()).collect::<Vec<String>>())
             }
         } else {
-            Err(Error::ExpectedString(robj))
+            Err(Error::ExpectedString(robj.clone()))
         }
     }
 }
 
-macro_rules! impl_option {
-    ($type : ty) => {
-        impl TryFrom<Robj> for Option<$type> {
-            type Error = Error;
+impl TryFrom<&Robj> for &[i32] {
+    type Error = Error;
 
-            /// Convert a scalar object that may be NA type to an `Option` of a corresponding type.
-            /// Returns `None` if the scalar is NA.
-            fn try_from(robj: Robj) -> Result<Self> {
-                if robj.is_na() {
-                    Ok(None)
-                } else {
-                    Ok(Some(<$type>::try_from(robj)?))
-                }
-            }
-        }
-    };
+    /// Convert an INTSXP object into a slice of i32 (integer).
+    /// Use `value.is_na()` to detect NA values.
+    fn try_from(robj: &Robj) -> Result<Self> {
+        robj.as_typed_slice()
+            .ok_or_else(|| Error::ExpectedInteger(robj.clone()))
+    }
 }
 
-impl_option!(u8);
-impl_option!(u16);
-impl_option!(u32);
-impl_option!(u64);
-impl_option!(i8);
-impl_option!(i16);
-impl_option!(i32);
-impl_option!(i64);
-impl_option!(f32);
-impl_option!(f64);
-impl_option!(Rbool);
-impl_option!(bool);
-impl_option!(&str);
-impl_option!(String);
-impl_option!(Vec<i32>);
-impl_option!(Vec<f64>);
-impl_option!(Vec<String>);
-
-impl TryFrom<Robj> for &[i32] {
+impl TryFrom<&Robj> for &[Rint] {
     type Error = Error;
 
-    /// Convert an INTSXP object into a slice of i32 (integer).
+    /// Convert an integer object into a slice of Rint (tri-state booleans).
     /// Use `value.is_na()` to detect NA values.
-    fn try_from(robj: Robj) -> Result<Self> {
-        robj.as_typed_slice().ok_or(Error::ExpectedInteger(robj))
+    fn try_from(robj: &Robj) -> Result<Self> {
+        robj.as_typed_slice()
+            .ok_or_else(|| Error::ExpectedInteger(robj.clone()))
     }
 }
 
-impl TryFrom<Robj> for &[Rbool] {
+impl TryFrom<&Robj> for &[Rfloat] {
     type Error = Error;
 
-    /// Convert a LGLSXP object into a slice of Rbool (tri-state booleans).
+    /// Convert a doubles object into a slice of Rfloat (tri-state booleans).
     /// Use `value.is_na()` to detect NA values.
-    fn try_from(robj: Robj) -> Result<Self> {
-        robj.as_typed_slice().ok_or(Error::ExpectedLogical(robj))
+    fn try_from(robj: &Robj) -> Result<Self> {
+        robj.as_typed_slice()
+            .ok_or_else(|| Error::ExpectedReal(robj.clone()))
     }
 }
 
-impl TryFrom<Robj> for &[u8] {
+impl TryFrom<&Robj> for &[Rbool] {
+    type Error = Error;
+
+    /// Convert a logical object into a slice of Rbool (tri-state booleans).
+    /// Use `value.is_na()` to detect NA values.
+    fn try_from(robj: &Robj) -> Result<Self> {
+        robj.as_typed_slice()
+            .ok_or_else(|| Error::ExpectedLogical(robj.clone()))
+    }
+}
+
+impl TryFrom<&Robj> for &[Rcplx] {
+    type Error = Error;
+
+    /// Convert a complex object into a slice of Rbool
+    /// Use `value.is_na()` to detect NA values.
+    fn try_from(robj: &Robj) -> Result<Self> {
+        robj.as_typed_slice()
+            .ok_or_else(|| Error::ExpectedComplex(robj.clone()))
+    }
+}
+
+impl TryFrom<&Robj> for &[u8] {
     type Error = Error;
 
     /// Convert a RAWSXP object into a slice of bytes.
-    fn try_from(robj: Robj) -> Result<Self> {
-        robj.as_typed_slice().ok_or(Error::ExpectedRaw(robj))
+    fn try_from(robj: &Robj) -> Result<Self> {
+        robj.as_typed_slice()
+            .ok_or_else(|| Error::ExpectedRaw(robj.clone()))
     }
 }
 
-impl TryFrom<Robj> for &[f64] {
+impl TryFrom<&Robj> for &[f64] {
     type Error = Error;
 
     /// Convert a REALSXP object into a slice of f64 (double precision floating point).
     /// Use `value.is_na()` to detect NA values.
-    fn try_from(robj: Robj) -> Result<Self> {
-        robj.as_typed_slice().ok_or(Error::ExpectedReal(robj))
+    fn try_from(robj: &Robj) -> Result<Self> {
+        robj.as_typed_slice()
+            .ok_or_else(|| Error::ExpectedReal(robj.clone()))
+    }
+}
+
+impl TryFrom<&Robj> for Rcplx {
+    type Error = Error;
+
+    fn try_from(robj: &Robj) -> Result<Self> {
+        // Check if the value is a scalar
+        match robj.len() {
+            0 => return Err(Error::ExpectedNonZeroLength(robj.clone())),
+            1 => {}
+            _ => return Err(Error::ExpectedScalar(robj.clone())),
+        };
+
+        // Check if the value is not a missing value.
+        if robj.is_na() {
+            return Ok(Rcplx::na());
+        }
+
+        // This should always work, NA is handled above.
+        if let Some(v) = robj.as_real() {
+            return Ok(Rcplx::from(v));
+        }
+
+        // Any integer (32 bit) can be represented as f64,
+        // this always works.
+        if let Some(v) = robj.as_integer() {
+            return Ok(Rcplx::from(v as f64));
+        }
+
+        // Complex slices return their first element.
+        if let Some(s) = robj.as_typed_slice() {
+            return Ok(s[0]);
+        }
+
+        Err(Error::ExpectedComplex(robj.clone()))
     }
 }
+
+// Convert TryFrom<&Robj> into TryFrom<&Robj>. Sadly, we are unable to make a blanket
+// conversion using GetSexp with the current version of Rust.
+macro_rules! impl_try_from_robj {
+    ($($type : ty)*) => {
+        $(
+            impl TryFrom<Robj> for $type {
+                type Error = Error;
+
+                fn try_from(robj: Robj) -> Result<Self> {
+                    <$type>::try_from(&robj)
+                }
+            }
+
+            impl TryFrom<&Robj> for Option<$type> {
+                type Error = Error;
+
+                fn try_from(robj: &Robj) -> Result<Self> {
+                    if robj.is_null() || robj.is_na() {
+                        Ok(None)
+                    } else {
+                        Ok(Some(<$type>::try_from(robj)?))
+                    }
+                }
+            }
+
+            impl TryFrom<Robj> for Option<$type> {
+                type Error = Error;
+
+                fn try_from(robj: Robj) -> Result<Self> {
+                    <Option::<$type>>::try_from(&robj)
+                }
+            }
+        )*
+    }
+}
+
+impl_try_from_robj!(
+    u8 u16 u32 u64 usize
+    i8 i16 i32 i64 isize
+    bool
+    Rint Rfloat Rbool Rcplx
+    f32 f64
+    Vec::<Rint> Vec::<Rfloat> Vec::<Rbool> Vec::<Rcplx> Vec::<u8> Vec::<i32> Vec::<f64>
+    &[Rint] &[Rfloat] &[Rbool] &[Rcplx] &[u8] &[i32] &[f64]
+    &str String
+);
diff --git a/extendr-api/src/scalar/rbool.rs b/extendr-api/src/scalar/rbool.rs
index f36dd85d2..46a50f494 100644
--- a/extendr-api/src/scalar/rbool.rs
+++ b/extendr-api/src/scalar/rbool.rs
@@ -109,3 +109,21 @@ impl std::fmt::Debug for Rbool {
         )
     }
 }
+
+impl TryFrom<&Robj> for Rbool {
+    type Error = Error;
+
+    /// Convert an LGLSXP object into a Rbool (tri-state boolean).
+    /// Use `value.is_na()` to detect NA values.
+    fn try_from(robj: &Robj) -> Result<Self> {
+        if let Some(v) = robj.as_logical_slice() {
+            match v.len() {
+                0 => Err(Error::ExpectedNonZeroLength(robj.clone())),
+                1 => Ok(v[0]),
+                _ => Err(Error::ExpectedScalar(robj.clone())),
+            }
+        } else {
+            Err(Error::ExpectedLogical(robj.clone()))
+        }
+    }
+}
diff --git a/extendr-api/src/scalar/rcplx_default.rs b/extendr-api/src/scalar/rcplx_default.rs
index 05e094806..e09bfd688 100644
--- a/extendr-api/src/scalar/rcplx_default.rs
+++ b/extendr-api/src/scalar/rcplx_default.rs
@@ -1,6 +1,5 @@
 use crate::scalar::Rfloat;
 use crate::*;
-use std::convert::TryFrom;
 
 #[allow(non_camel_case_types)]
 #[derive(Clone, Copy, Debug, Default, PartialEq)]
@@ -113,42 +112,6 @@ impl From<Rcplx> for Option<c64> {
     }
 }
 
-impl TryFrom<Robj> for Rcplx {
-    type Error = Error;
-
-    fn try_from(robj: Robj) -> Result<Self> {
-        // Check if the value is a scalar
-        match robj.len() {
-            0 => return Err(Error::ExpectedNonZeroLength(robj)),
-            1 => {}
-            _ => return Err(Error::ExpectedScalar(robj)),
-        };
-
-        // Check if the value is not a missing value.
-        if robj.is_na() {
-            return Ok(Rcplx::na());
-        }
-
-        // This should always work, NA is handled above.
-        if let Some(v) = robj.as_real() {
-            return Ok(Rcplx::from(v));
-        }
-
-        // Any integer (32 bit) can be represented as f64,
-        // this always works.
-        if let Some(v) = robj.as_integer() {
-            return Ok(Rcplx::from(v as f64));
-        }
-
-        // Complex slices return their first element.
-        if let Some(s) = robj.as_typed_slice() {
-            return Ok(s[0]);
-        }
-
-        Err(Error::ExpectedComplex(robj))
-    }
-}
-
 impl PartialEq<f64> for Rcplx {
     fn eq(&self, other: &f64) -> bool {
         self.re().inner() == *other && self.im() == 0.0
diff --git a/extendr-api/src/scalar/rcplx_full.rs b/extendr-api/src/scalar/rcplx_full.rs
index dc35d1291..18047f217 100644
--- a/extendr-api/src/scalar/rcplx_full.rs
+++ b/extendr-api/src/scalar/rcplx_full.rs
@@ -154,42 +154,6 @@ gen_binopassign!(
 // Generate unary ops for -, !
 gen_unop!(Rcplx, Neg, |lhs: c64| Some(-lhs), "Negate a Rcplx value.");
 
-impl TryFrom<Robj> for Rcplx {
-    type Error = Error;
-
-    fn try_from(robj: Robj) -> Result<Self> {
-        // Check if the value is a scalar
-        match robj.len() {
-            0 => return Err(Error::ExpectedNonZeroLength(robj)),
-            1 => {}
-            _ => return Err(Error::ExpectedScalar(robj)),
-        };
-
-        // Check if the value is not a missing value.
-        if robj.is_na() {
-            return Ok(Rcplx::na());
-        }
-
-        // This should always work, NA is handled above.
-        if let Some(v) = robj.as_real() {
-            return Ok(Rcplx::from(v));
-        }
-
-        // Any integer (32 bit) can be represented as f64,
-        // this always works.
-        if let Some(v) = robj.as_integer() {
-            return Ok(Rcplx::from(v as f64));
-        }
-
-        // Complex slices return their first element.
-        if let Some(s) = robj.as_typed_slice() {
-            return Ok(s[0]);
-        }
-
-        Err(Error::ExpectedComplex(robj))
-    }
-}
-
 impl PartialEq<f64> for Rcplx {
     fn eq(&self, other: &f64) -> bool {
         self.re().inner() == *other && self.im() == 0.0
diff --git a/extendr-api/src/scalar/rfloat.rs b/extendr-api/src/scalar/rfloat.rs
index 5e1b291ba..71e457fd5 100644
--- a/extendr-api/src/scalar/rfloat.rs
+++ b/extendr-api/src/scalar/rfloat.rs
@@ -100,15 +100,15 @@ gen_binopassign!(
 // Generate unary ops for -, !
 gen_unop!(Rfloat, Neg, |lhs: f64| Some(-lhs), "Negate a Rfloat value.");
 
-impl TryFrom<Robj> for Rfloat {
+impl TryFrom<&Robj> for Rfloat {
     type Error = Error;
 
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         // Check if the value is a scalar
         match robj.len() {
-            0 => return Err(Error::ExpectedNonZeroLength(robj)),
+            0 => return Err(Error::ExpectedNonZeroLength(robj.clone())),
             1 => {}
-            _ => return Err(Error::ExpectedScalar(robj)),
+            _ => return Err(Error::ExpectedScalar(robj.clone())),
         };
 
         // Check if the value is not a missing value.
@@ -127,7 +127,7 @@ impl TryFrom<Robj> for Rfloat {
             return Ok(Rfloat::from(v as f64));
         }
 
-        Err(Error::ExpectedNumeric(robj))
+        Err(Error::ExpectedNumeric(robj.clone()))
     }
 }
 
diff --git a/extendr-api/src/scalar/rint.rs b/extendr-api/src/scalar/rint.rs
index 65ce3b66d..6dc9d710b 100644
--- a/extendr-api/src/scalar/rint.rs
+++ b/extendr-api/src/scalar/rint.rs
@@ -94,15 +94,15 @@ gen_unop!(
     "Logical not a Rint value, overflows to NA."
 );
 
-impl TryFrom<Robj> for Rint {
+impl TryFrom<&Robj> for Rint {
     type Error = Error;
 
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         // Check if the value is a scalar
         match robj.len() {
-            0 => return Err(Error::ExpectedNonZeroLength(robj)),
+            0 => return Err(Error::ExpectedNonZeroLength(robj.clone())),
             1 => {}
-            _ => return Err(Error::ExpectedScalar(robj)),
+            _ => return Err(Error::ExpectedScalar(robj.clone())),
         };
 
         // Check if the value is not a missing value
@@ -118,7 +118,7 @@ impl TryFrom<Robj> for Rint {
             if let Ok(v) = Self::try_from(v) {
                 return Ok(v);
             } else {
-                return Err(Error::OutOfLimits(robj));
+                return Err(Error::OutOfLimits(robj.clone()));
             }
         }
 
@@ -132,11 +132,11 @@ impl TryFrom<Robj> for Rint {
             if (result as f64 - v).abs() < f64::EPSILON {
                 return Ok(Rint::from(result));
             } else {
-                return Err(Error::ExpectedWholeNumber(robj));
+                return Err(Error::ExpectedWholeNumber(robj.clone()));
             }
         }
 
-        Err(Error::ExpectedNumeric(robj))
+        Err(Error::ExpectedNumeric(robj.clone()))
     }
 }
 
diff --git a/extendr-api/src/wrapper/externalptr.rs b/extendr-api/src/wrapper/externalptr.rs
index 875911dd4..be8345d5a 100644
--- a/extendr-api/src/wrapper/externalptr.rs
+++ b/extendr-api/src/wrapper/externalptr.rs
@@ -145,16 +145,16 @@ impl<T: Any + Debug> ExternalPtr<T> {
     }
 }
 
-impl<T: Any + Debug> TryFrom<Robj> for ExternalPtr<T> {
+impl<T: Any + Debug> TryFrom<&Robj> for ExternalPtr<T> {
     type Error = Error;
 
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         if robj.rtype() != Rtype::ExternalPtr {
-            return Err(Error::ExpectedExternalPtr(robj));
+            return Err(Error::ExpectedExternalPtr(robj.clone()));
         }
 
         let res = ExternalPtr::<T> {
-            robj,
+            robj: robj.clone(),
             marker: std::marker::PhantomData,
         };
 
@@ -168,6 +168,14 @@ impl<T: Any + Debug> TryFrom<Robj> for ExternalPtr<T> {
     }
 }
 
+impl<T: Any + Debug> TryFrom<Robj> for ExternalPtr<T> {
+    type Error = Error;
+
+    fn try_from(robj: Robj) -> Result<Self> {
+        <ExternalPtr<T>>::try_from(&robj)
+    }
+}
+
 impl<T: Any + Debug> From<ExternalPtr<T>> for Robj {
     fn from(val: ExternalPtr<T>) -> Self {
         val.robj
diff --git a/extendr-api/src/wrapper/integers.rs b/extendr-api/src/wrapper/integers.rs
index a2667c57e..55d7f05e7 100644
--- a/extendr-api/src/wrapper/integers.rs
+++ b/extendr-api/src/wrapper/integers.rs
@@ -66,7 +66,7 @@ mod tests {
     fn iter_mut() {
         test! {
             let mut vec = Integers::from_values(0..3);
-            vec.iter_mut().for_each(|v| *v = *v + 1);
+            vec.iter_mut().for_each(|v| *v += 1);
             assert_eq!(vec, Integers::from_values(1..4));
         }
     }
diff --git a/extendr-api/src/wrapper/list.rs b/extendr-api/src/wrapper/list.rs
index 4e848da9c..e1568123b 100644
--- a/extendr-api/src/wrapper/list.rs
+++ b/extendr-api/src/wrapper/list.rs
@@ -295,23 +295,14 @@ impl ExactSizeIterator for ListIter {
 /// ```
 pub struct FromList<T>(pub T);
 
-impl<T> TryFrom<Robj> for FromList<Vec<T>>
+impl<T> TryFrom<&Robj> for FromList<Vec<T>>
 where
     T: TryFrom<Robj>,
     <T as TryFrom<Robj>>::Error: Into<Error>,
 {
     type Error = Error;
 
-    /// You can use the FromList wrapper to coerce a Robj into a list.
-    /// ```
-    /// use extendr_api::prelude::*;
-    /// test! {
-    ///     let list = Robj::from(list!(1, 2));
-    ///     let vec : FromList<Vec<i32>> = list.try_into()?;
-    ///     assert_eq!(vec.0, vec![1, 2]);
-    /// }
-    /// ```
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         let listiter: ListIter = robj.try_into()?;
         let res: Result<Vec<_>> = listiter
             .map(|robj| T::try_from(robj).map_err(|e| e.into()))
@@ -320,24 +311,37 @@ where
     }
 }
 
-impl TryFrom<Robj> for ListIter {
+impl<T> TryFrom<Robj> for FromList<Vec<T>>
+where
+    T: TryFrom<Robj>,
+    <T as TryFrom<Robj>>::Error: Into<Error>,
+{
     type Error = Error;
 
-    /// You can pass a ListIter to a function.
-    /// ```
-    /// use extendr_api::prelude::*;
-    /// test! {
-    ///     let list = Robj::from(list!(1, 2));
-    ///     let vec : ListIter = list.try_into()?;
-    ///     assert_eq!(vec.collect::<Vec<_>>(), vec![r!(1), r!(2)]);
-    /// }
-    /// ```
     fn try_from(robj: Robj) -> Result<Self> {
+        <FromList<Vec<T>>>::try_from(&robj)
+    }
+}
+
+impl TryFrom<&Robj> for ListIter {
+    type Error = Error;
+
+    /// Convert a general R object into a List iterator if possible.
+    fn try_from(robj: &Robj) -> Result<Self> {
         let list: List = robj.try_into()?;
         Ok(list.values())
     }
 }
 
+impl TryFrom<Robj> for ListIter {
+    type Error = Error;
+
+    /// Convert a general R object into a List iterator if possible.
+    fn try_from(robj: Robj) -> Result<Self> {
+        <ListIter>::try_from(&robj)
+    }
+}
+
 impl From<ListIter> for Robj {
     /// You can return a ListIter from a function.
     /// ```
diff --git a/extendr-api/src/wrapper/mod.rs b/extendr-api/src/wrapper/mod.rs
index 0d9739f74..37663963e 100644
--- a/extendr-api/src/wrapper/mod.rs
+++ b/extendr-api/src/wrapper/mod.rs
@@ -104,32 +104,25 @@ macro_rules! make_conversions {
             }
         }
 
-        impl TryFrom<Robj> for $typename {
+        impl TryFrom<&Robj> for $typename {
             type Error = crate::Error;
 
             /// Make a wrapper from a robj if it matches.
-            fn try_from(robj: Robj) -> Result<Self> {
+            fn try_from(robj: &Robj) -> Result<Self> {
                 if robj.$isfunc() {
-                    Ok($typename { robj })
+                    Ok($typename { robj: robj.clone() })
                 } else {
-                    Err(Error::$errname(robj))
+                    Err(Error::$errname(robj.clone()))
                 }
             }
         }
 
-        // We can convert a borrowed Robj to any wrapper by cloning the pointer
-        impl TryFrom<&Robj> for $typename {
+        impl TryFrom<Robj> for $typename {
             type Error = crate::Error;
 
             /// Make a wrapper from a robj if it matches.
-            fn try_from(robj: &Robj) -> Result<Self> {
-                if robj.$isfunc() {
-                    Ok($typename {
-                        robj: robj.to_owned(),
-                    })
-                } else {
-                    Err(Error::$errname(robj.to_owned()))
-                }
+            fn try_from(robj: Robj) -> Result<Self> {
+                <$typename>::try_from(&robj)
             }
         }
 
diff --git a/extendr-api/src/wrapper/pairlist.rs b/extendr-api/src/wrapper/pairlist.rs
index 969b08988..d29f4b1db 100644
--- a/extendr-api/src/wrapper/pairlist.rs
+++ b/extendr-api/src/wrapper/pairlist.rs
@@ -175,11 +175,11 @@ impl IntoIterator for Pairlist {
     }
 }
 
-impl TryFrom<Robj> for PairlistIter {
+impl TryFrom<&Robj> for PairlistIter {
     type Error = Error;
 
     /// You can pass a PairlistIter to a function.
-    fn try_from(robj: Robj) -> Result<Self> {
+    fn try_from(robj: &Robj) -> Result<Self> {
         let pairlist: Pairlist = robj.try_into()?;
         Ok(pairlist.into_iter())
     }


Test patch:
diff --git a/extendr-api/src/robj/tests.rs b/extendr-api/src/robj/tests.rs
index 28c116399..9f6626a90 100644
--- a/extendr-api/src/robj/tests.rs
+++ b/extendr-api/src/robj/tests.rs
@@ -1,3 +1,4 @@
+use crate::scalar::*;
 use crate::*;
 
 #[test]
@@ -115,8 +116,8 @@ fn test_try_from_robj() {
         let robj = Robj::from(-1);
         assert_eq!(<u32>::try_from(robj.clone()), Err(Error::OutOfLimits(robj)));
 
-        assert_eq!(<Vec::<i32>>::try_from(Robj::from(1)), Ok(vec![1]));
-        assert_eq!(<Vec::<f64>>::try_from(Robj::from(1.)), Ok(vec![1.]));
+        assert_eq!(<Vec::<Rint>>::try_from(Robj::from(1)), Ok(vec![Rint::from(1)]));
+        assert_eq!(<Vec::<Rfloat>>::try_from(Robj::from(1.)), Ok(vec![Rfloat::from(1.0)]));
         assert_eq!(<Vec::<Rbool>>::try_from(Robj::from(TRUE)), Ok(vec![TRUE]));
         assert_eq!(<Vec::<u8>>::try_from(Robj::from(0_u8)), Ok(vec![0_u8]));
 
@@ -137,14 +138,14 @@ fn test_try_from_robj() {
         // assert_eq!(<Logicals>::try_from(r!([true, false])).unwrap().collect::<Vec<Rbool>>(), vec![TRUE, FALSE]);
         // assert!(<Logicals>::try_from(r!([1])).is_err());
 
-        assert_eq!(<&[i32]>::try_from(Robj::from(1)), Ok(&[1][..]));
-        assert_eq!(<&[f64]>::try_from(Robj::from(1.)), Ok(&[1.][..]));
+        assert_eq!(<&[Rint]>::try_from(Robj::from(1)), Ok(&[Rint::from(1)][..]));
+        assert_eq!(<&[Rfloat]>::try_from(Robj::from(1.)), Ok(&[Rfloat::from(1.)][..]));
         assert_eq!(<&[Rbool]>::try_from(Robj::from(TRUE)), Ok(&[TRUE][..]));
         assert_eq!(<&[u8]>::try_from(Robj::from(0_u8)), Ok(&[0_u8][..]));
 
         // Note the Vec<> cases use the same logic as the slices.
-        assert_eq!(<&[i32]>::try_from(Robj::from(1.0)), Err(Error::ExpectedInteger(r!(1.0))));
-        assert_eq!(<&[f64]>::try_from(Robj::from(1)), Err(Error::ExpectedReal(r!(1))));
+        assert_eq!(<&[Rint]>::try_from(Robj::from(1.0)), Err(Error::ExpectedInteger(r!(1.0))));
+        assert_eq!(<&[Rfloat]>::try_from(Robj::from(1)), Err(Error::ExpectedReal(r!(1))));
         assert_eq!(<&[Rbool]>::try_from(Robj::from(())), Err(Error::ExpectedLogical(r!(()))));
         assert_eq!(<&[u8]>::try_from(Robj::from(())), Err(Error::ExpectedRaw(r!(()))));
 
@@ -184,31 +185,31 @@ fn test_try_from_robj() {
 
         let na_integer = eval_string("NA_integer_").unwrap();
         assert!(<i32>::try_from(na_integer.clone()).is_err());
-        assert_eq!(<Option<i32>>::try_from(na_integer.clone()), Ok(None));
+        assert_eq!(<Option<i32>>::try_from(na_integer), Ok(None));
         assert_eq!(<Option<i32>>::try_from(Robj::from(1)), Ok(Some(1)));
         assert!(<Option<i32>>::try_from(Robj::from([1, 2])).is_err());
 
         let na_bool = eval_string("TRUE == NA").unwrap();
         assert!(<bool>::try_from(na_bool.clone()).is_err());
-        assert_eq!(<Option<bool>>::try_from(na_bool.clone()), Ok(None));
+        assert_eq!(<Option<bool>>::try_from(na_bool), Ok(None));
         assert_eq!(<Option<bool>>::try_from(Robj::from(true)), Ok(Some(true)));
         assert!(<Option<bool>>::try_from(Robj::from([true, false])).is_err());
 
         let na_real = eval_string("NA_real_").unwrap();
         assert!(<f64>::try_from(na_real.clone()).is_err());
-        assert_eq!(<Option<f64>>::try_from(na_real.clone()), Ok(None));
+        assert_eq!(<Option<f64>>::try_from(na_real), Ok(None));
         assert_eq!(<Option<f64>>::try_from(Robj::from(1.)), Ok(Some(1.)));
         assert!(<Option<f64>>::try_from(Robj::from([1., 2.])).is_err());
 
         let na_string = eval_string("NA_character_").unwrap();
         assert!(<&str>::try_from(na_string.clone()).is_err());
-        assert_eq!(<Option<&str>>::try_from(na_string.clone()), Ok(None));
+        assert_eq!(<Option<&str>>::try_from(na_string), Ok(None));
         assert_eq!(<Option<&str>>::try_from(Robj::from("1")), Ok(Some("1")));
         assert!(<Option<&str>>::try_from(Robj::from(["1", "2"])).is_err());
 
         let na_string = eval_string("NA_character_").unwrap();
         assert!(<String>::try_from(na_string.clone()).is_err());
-        assert_eq!(<Option<String>>::try_from(na_string.clone()), Ok(None));
+        assert_eq!(<Option<String>>::try_from(na_string), Ok(None));
         assert_eq!(
             <Option<String>>::try_from(Robj::from("1")),
             Ok(Some("1".to_string()))
diff --git a/extendr-api/tests/altrep_tests.rs b/extendr-api/tests/altrep_tests.rs
index 58497c8d4..d35892d3d 100644
--- a/extendr-api/tests/altrep_tests.rs
+++ b/extendr-api/tests/altrep_tests.rs
@@ -41,7 +41,7 @@ fn test_altinteger() {
         let mystate_w_missing = MyCompactIntRange { start: 0, len: 10, step: 1, missing_index: 5 };
 
         let obj_w_missing = Altrep::from_state_and_class(mystate_w_missing, class, false);
-        let robj_w_missing = Robj::from(obj_w_missing.clone());
+        let robj_w_missing = Robj::from(obj_w_missing);
         let integers_w_missing: Integers = robj_w_missing.try_into()?;
         assert_eq!(integers_w_missing.elt(9), Rint::from(9));
         assert!(integers_w_missing.elt(5).is_na());
@@ -95,7 +95,7 @@ fn test_altreal() {
         let mystate_w_missing = MyCompactRealRange { start: 0.0, len: 10, step: 1.0, missing_index: 5 };
 
         let obj_w_missing = Altrep::from_state_and_class(mystate_w_missing, class, false);
-        let robj_w_missing = Robj::from(obj_w_missing.clone());
+        let robj_w_missing = Robj::from(obj_w_missing);
         let doubles_w_missing: Doubles = robj_w_missing.try_into()?;
         assert_eq!(doubles_w_missing.elt(9), Rfloat::from(9.0));
 
diff --git a/extendr-api/tests/extendr_macro.rs b/extendr-api/tests/extendr_macro.rs
index a6bfbab17..234a3ed33 100644
--- a/extendr-api/tests/extendr_macro.rs
+++ b/extendr-api/tests/extendr_macro.rs
@@ -202,7 +202,7 @@ fn test_metadata() {
             rust_name: "test_metadata_1",
             mod_name: "test_metadata_1",
             r_name: "test_metadata_1",
-            args: args,
+            args,
             return_type: "i32",
             func_ptr: wrap__test_metadata_1 as *const u8,
             hidden: false,
diff --git a/extendr-api/tests/try_from_tests.rs b/extendr-api/tests/try_from_tests.rs
new file mode 100644
index 000000000..fe37ce660
--- /dev/null
+++ b/extendr-api/tests/try_from_tests.rs
@@ -0,0 +1,144 @@
+//! See https://github.com/extendr/extendr/issues/369
+//!
+
+#[test]
+fn test_try_from() {
+    use extendr_api::scalar::{Rbool, Rcplx, Rfloat, Rint};
+    use extendr_api::{r, test, Result, Robj, TryFrom};
+    // use extendr_api::wrapper::{Integers, Doubles, Strings};
+
+    macro_rules! test_matrix {
+        (
+            $value : expr,
+            int_ok: $int_ok : expr,
+            float_ok: $float_ok : expr,
+            cplx_ok: $cplx_ok : expr,
+            bool_ok: $bool_ok : expr,
+            str_ok: $str_ok : expr,
+            int_slice_ok: $int_slice_ok : expr,
+            float_slice_ok: $float_slice_ok : expr,
+        ) => {
+            assert!(u8::try_from($value).is_ok() == $int_ok);
+            assert!(i8::try_from($value).is_ok() == $int_ok);
+            assert!(u16::try_from($value).is_ok() == $int_ok);
+            assert!(i16::try_from($value).is_ok() == $int_ok);
+            assert!(u32::try_from($value).is_ok() == $int_ok);
+            assert!(i32::try_from($value).is_ok() == $int_ok);
+            assert!(u64::try_from($value).is_ok() == $int_ok);
+            assert!(i64::try_from($value).is_ok() == $int_ok);
+            // assert!(usize::try_from($value).is_ok() == $int_ok);
+            // assert!(isize::try_from($value).is_ok() == $int_ok);
+            assert!(f32::try_from($value).is_ok() == $float_ok);
+            assert!(f64::try_from($value).is_ok() == $float_ok);
+            assert!(Rint::try_from($value).is_ok() == $int_ok);
+            assert!(Rfloat::try_from($value).is_ok() == $float_ok);
+            assert!(Rcplx::try_from($value).is_ok() == $cplx_ok);
+            // assert!(<&Rstr>::try_from($value).is_ok() == $str_ok);
+            assert!(Rbool::try_from($value).is_ok() == $bool_ok);
+            assert!(bool::try_from($value).is_ok() == $bool_ok);
+            assert!(<&str>::try_from($value).is_ok() == $str_ok);
+            assert!(String::try_from($value).is_ok() == $str_ok);
+            assert!(<&[i32]>::try_from($value).is_ok() == $int_slice_ok);
+            assert!(<&[f64]>::try_from($value).is_ok() == $float_slice_ok);
+            // assert!(<&Robj>::try_from($value).is_ok() == true);
+            assert!(<Robj>::try_from($value).is_ok() == true);
+            // assert!(<&[Rint]>::try_from($value).is_ok() == $int_slice_ok);
+            // assert!(<&[Rfloat]>::try_from($value).is_ok() == $float_slice_ok);
+            // assert!(<&[Rcplx]>::try_from($value).is_ok() == $cplx_slice_ok);
+            // assert!(<&[Rbool]>::try_from($value).is_ok() == $bool_slice_ok);
+            // assert!(<&[Rstr]>::try_from($value).is_ok() == $str_ok);
+            // assert!(<&[Robj]>::try_from($value).is_ok() == $list_ok);
+            // assert!(<&Integers>::try_from($value).is_ok() == $int_ok);
+            // assert!(<&Doubles>::try_from($value).is_ok() == $float_ok);
+            // assert!(<&List>::try_from($value).is_ok() == $list_ok);
+
+            // assert_eq!(<Option<u8>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<i8>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<u16>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<i16>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<u32>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<i32>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<u64>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<i64>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<usize>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<isize>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<Rint>::try_from($value).is_ok(), $int_ok);
+            // assert_eq!(<Option<f32>::try_from($value).is_ok(), $float_ok);
+            // assert_eq!(<Option<f64>::try_from($value).is_ok(), $float_ok);
+            // assert_eq!(<Option<Rfloat>::try_from($value).is_ok(), $float_ok);
+            // assert_eq!(<Option<Rcplx>::try_from($value).is_ok(), $float_ok);
+            // assert_eq!(<Option<bool>::try_from($value).is_ok(), $bool_ok);
+            // assert_eq!(<Option<&str>>::try_from($value).is_ok(), $str_ok);
+            // assert_eq!(<Option<String>::try_from($value).is_ok(), $str_ok);
+        };
+    }
+
+    test! {
+        let integer = r!(1);
+        test_matrix!(
+            integer.clone(),
+            int_ok : true,
+            float_ok : true,
+            cplx_ok: true,
+            bool_ok : false,
+            str_ok: false,
+            int_slice_ok: true,
+            float_slice_ok: false,
+        );
+
+        let double = r!(1.0);
+        test_matrix!(
+            double.clone(),
+            int_ok : true,
+            float_ok : true,
+            cplx_ok: true,
+            bool_ok : false,
+            str_ok: false,
+            int_slice_ok: false,
+            float_slice_ok: true,
+        );
+
+        let null = r!(());
+        test_matrix!(
+            null.clone(),
+            int_ok : false,
+            float_ok : false,
+            cplx_ok: false,
+            bool_ok : false,
+            str_ok: false,
+            int_slice_ok: false,
+            float_slice_ok: false,
+        );
+
+        let string = r!("1");
+        test_matrix!(
+            string.clone(),
+            int_ok : false,
+            float_ok : false,
+            cplx_ok: false,
+            bool_ok : false,
+            str_ok: true,
+            int_slice_ok: false,
+            float_slice_ok: false,
+        );
+
+        // let integers = Integers::from_values([1]);
+        // test_matrix!(
+        //     integer.clone(),
+        //     int_ok : true,
+        //     float_ok : true,
+        //     cplx_ok: true,
+        //     bool_ok : false,
+        //     str_ok: false,
+        //     int_slice_ok: true,
+        //     float_slice_ok: false,
+        // );
+        // test_matrix!(integers.clone(), int_ok : true, float_ok : true, bool_ok : false, str_ok: false);
+
+        // let doubles = Doubles::from_values([1.0]);
+        // test_matrix!(doubles.clone(), int_ok : true, float_ok : true, bool_ok : false, str_ok: false);
+
+        // let strings = Strings::from_values(["1"]);
+        // test_matrix!(strings.clone(), int_ok : true, float_ok : true, bool_ok : false, str_ok: false);
+    }
+}
diff --git a/extendr-api/tests/vector_tests.rs b/extendr-api/tests/vector_tests.rs
index 1f3e5e38d..60db96cdc 100644
--- a/extendr-api/tests/vector_tests.rs
+++ b/extendr-api/tests/vector_tests.rs
@@ -54,7 +54,7 @@ fn test_list() {
         assert_eq!(s.elt(2)?, r!("z"));
         assert_eq!(s.elt(3).is_err(), true);
 
-        let v = s.as_slice().iter().map(|c| c).collect::<Vec<_>>();
+        let v = s.as_slice().iter().collect::<Vec<_>>();
         assert_eq!(v, vec![&r!("x"), &r!("y"), &r!("z")]);
 
         s.set_elt(1, r!("q"))?;
@@ -71,7 +71,7 @@ fn test_list() {
         assert_eq!(s.as_slice().iter().any(|s| s.is_na()), true);
 
         // Deref allows all the immutable methods from slice.
-        let v = s.as_slice().iter().map(|c| c).collect::<Vec<_>>();
+        let v = s.as_slice().iter().collect::<Vec<_>>();
         assert_eq!(v, vec![&r!("x"), &r!(<&str>::na()), &r!("z")]);
         assert_eq!(v[0], "x");
         assert_eq!(v[1].is_na(), true);
@@ -253,7 +253,7 @@ fn test_doubles_from_iterator() {
 fn test_doubles_iter_mut() {
     test! {
         let mut vec = Doubles::from_values([0.0, 1.0, 2.0, 3.0]);
-        vec.iter_mut().for_each(|v| *v = *v + 1.0);
+        vec.iter_mut().for_each(|v| *v += 1.0);
         assert_eq!(vec, Doubles::from_values([1.0, 2.0, 3.0, 4.0]));
     }
 }