Repository: extendr/extendr
Pull request #833 (2024-08-03 18:22:45)

Add missing impls to `ExternalPtr<T>`
Missing impls that are useful in `#[extendr]`-impl, that should be accessible via `TryFrom<Robj>` too!

This was caught by @kbvernon and the code in the R test is from them.

Long explanation: The `#[extendr]` macro knows that it needs to convert from `&Robj`/`&mut Robj`. Usually, we don't do that. What we do is we implement things from `TryFrom<&Robj>` and piggyback that on `TryFrom<Robj>`, because `#[extendr]` usually just use `Robj` directly. 

And therefore, we forgot to include these impls.




Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65d9eab2d..aafe43edf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,10 +4,13 @@
 
 ### Added
 
+- `TryFrom<Robj>` is now implemented for `&ExternalPtr<T>` and `&mut ExternalPtr<T>` <https://github.com/extendr/extendr/pull/833>
 - `Option<T>` can now be returned from `#[extendr]` annotated functions for extendr_api types. `None` is translated into `NULL` value in R [[#802]](https://github.com/extendr/extendr/pull/802)
 
+
 ### Changed
 
+- Enhancement: string comparisons are now implemented for `Rstr` using R's string intern mechanism. Making string comparisons _much_ faster. [[#845]](https://github.com/extendr/extendr/pull/845)
 - Breaking change: `RMatrix::get_rownames` and `RMatrix::get_colnames` now both
 return `Option<Strings>` instead of opaque `Robj`.
 [[#801]](https://github.com/extendr/extendr/pull/790)
diff --git a/Cargo.toml b/Cargo.toml
index 985ed4131..2106660ac 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ resolver = "2"
 members = ["extendr-api", "extendr-engine", "extendr-macros", "xtask"]
 
 [workspace.package]
-version = "0.7.0"
+version = "0.7.1"
 authors = [
     "andy-thomason <andy@andythomason.com>",
     "Thomas Down",
@@ -20,9 +20,9 @@ repository = "https://github.com/extendr/extendr"
 
 [workspace.dependencies]
 # When updating extendr's version, this version also needs to be updated
-extendr-macros = { path = "./extendr-macros", version = "0.7.0" }
+extendr-macros = { path = "./extendr-macros", version = "0.7.1" }
 
 # When uncommenting this, do not forget to uncomment the same line in
 # ./tests/extendrtests/src/rust/Cargo.toml, and "Run R integration tests using
 # {rextendr}" on .github/workflows/test.yml !
-libR-sys = { version = "0.7" }
+libR-sys = { version = "0.7.1" }
diff --git a/extendr-api/Cargo.toml b/extendr-api/Cargo.toml
index 51b379f4e..2b6166f66 100644
--- a/extendr-api/Cargo.toml
+++ b/extendr-api/Cargo.toml
@@ -24,7 +24,7 @@ faer = { version = "0.19", optional = true }
 
 [dev-dependencies]
 extendr-engine = { path = "../extendr-engine" }
-rstest = "0.19.0"
+rstest = "0.22.0"
 
 [features]
 
diff --git a/extendr-api/src/iter.rs b/extendr-api/src/iter.rs
index 203301852..425dd9d7d 100644
--- a/extendr-api/src/iter.rs
+++ b/extendr-api/src/iter.rs
@@ -145,6 +145,14 @@ impl TryFrom<&Robj> for StrIter {
     }
 }
 
+impl TryFrom<Robj> for StrIter {
+    type Error = Error;
+
+    fn try_from(value: Robj) -> Result<Self> {
+        (&value).try_into()
+    }
+}
+
 pub trait AsStrIter: GetSexp + Types + Length + Attributes + Rinternals {
     /// Get an iterator over a string vector.
     /// Returns None if the object is not a string vector
diff --git a/extendr-api/src/robj/rinternals.rs b/extendr-api/src/robj/rinternals.rs
index 8f6bdc345..c584c4bd6 100644
--- a/extendr-api/src/robj/rinternals.rs
+++ b/extendr-api/src/robj/rinternals.rs
@@ -354,11 +354,13 @@ pub trait Rinternals: Types + Conversions {
         unsafe { Rf_isUserBinop(self.get()).into() }
     }
 
+    #[cfg(feature = "non-api")]
     /// Return true if this is a valid string.
     fn is_valid_string(&self) -> bool {
         unsafe { Rf_isValidString(self.get()).into() }
     }
 
+    #[cfg(feature = "non-api")]
     /// Return true if this is a valid string.
     fn is_valid_string_f(&self) -> bool {
         unsafe { Rf_isValidStringF(self.get()).into() }
diff --git a/extendr-api/src/wrapper/externalptr.rs b/extendr-api/src/wrapper/externalptr.rs
index 7f6a97493..f1a83ce29 100644
--- a/extendr-api/src/wrapper/externalptr.rs
+++ b/extendr-api/src/wrapper/externalptr.rs
@@ -244,6 +244,22 @@ impl<T> TryFrom<&mut Robj> for &mut ExternalPtr<T> {
     }
 }
 
+impl<T> TryFrom<Robj> for &ExternalPtr<T> {
+    type Error = Error;
+
+    fn try_from(value: Robj) -> Result<Self> {
+        (&value).try_into()
+    }
+}
+
+impl<T> TryFrom<Robj> for &mut ExternalPtr<T> {
+    type Error = Error;
+
+    fn try_from(mut value: Robj) -> Result<Self> {
+        (&mut value).try_into()
+    }
+}
+
 impl<T> TryFrom<&Robj> for ExternalPtr<T> {
     type Error = Error;
 
diff --git a/extendr-api/src/wrapper/rstr.rs b/extendr-api/src/wrapper/rstr.rs
index d9108d35e..ded920a7b 100644
--- a/extendr-api/src/wrapper/rstr.rs
+++ b/extendr-api/src/wrapper/rstr.rs
@@ -100,16 +100,15 @@ impl Deref for Rstr {
     }
 }
 
-impl<T> PartialEq<T> for Rstr
-where
-    T: AsRef<str>,
-{
-    /// Compare a `Rstr` with a `Rstr`.
-    fn eq(&self, other: &T) -> bool {
-        self.as_str() == other.as_ref()
+/// Defer comparison to R's string interner
+impl PartialEq<Rstr> for Rstr {
+    fn eq(&self, other: &Rstr) -> bool {
+        unsafe { self.robj.get() == other.robj.get() }
     }
 }
 
+/// Let performant than comparing [Rstr] directly as
+/// we need to convert [Rstr] to a string slice first
 impl PartialEq<str> for Rstr {
     /// Compare a `Rstr` with a string slice.
     fn eq(&self, other: &str) -> bool {
@@ -117,6 +116,27 @@ impl PartialEq<str> for Rstr {
     }
 }
 
+impl PartialEq<Rstr> for &str {
+    /// Compare a `Rstr` with a string slice.
+    fn eq(&self, other: &Rstr) -> bool {
+        *self == other.as_str()
+    }
+}
+
+impl PartialEq<&str> for Rstr {
+    /// Compare a `Rstr` with a string slice.
+    fn eq(&self, other: &&str) -> bool {
+        self.as_str() == *other
+    }
+}
+
+impl PartialEq<Rstr> for &&str {
+    /// Compare a `Rstr` with a string slice.
+    fn eq(&self, other: &Rstr) -> bool {
+        **self == other.as_str()
+    }
+}
+
 impl std::fmt::Debug for Rstr {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         if self.is_na() {


Test patch:
diff --git a/tests/extendrtests/.Rbuildignore b/tests/extendrtests/.Rbuildignore
index e13c4051c..7615186f7 100644
--- a/tests/extendrtests/.Rbuildignore
+++ b/tests/extendrtests/.Rbuildignore
@@ -1,3 +1,4 @@
 ^.*\.Rproj$
 ^\.Rproj\.user$
 ^LICENSE\.md$
+^notebooks$
diff --git a/tests/extendrtests/R/extendr-wrappers.R b/tests/extendrtests/R/extendr-wrappers.R
index 263cca4ce..dc404d1fd 100644
--- a/tests/extendrtests/R/extendr-wrappers.R
+++ b/tests/extendrtests/R/extendr-wrappers.R
@@ -159,6 +159,10 @@ logicals_sum <- function(logicals) .Call(wrap__logicals_sum, logicals)
 
 floats_mean <- function(x) .Call(wrap__floats_mean, x)
 
+to_unique_rstr <- function(r_char_vec) .Call(wrap__to_unique_rstr, r_char_vec)
+
+to_unique_str <- function(r_char_vec) .Call(wrap__to_unique_str, r_char_vec)
+
 #' Class for testing (exported)
 #' @examples
 #' x <- MyClass$new()
diff --git a/tests/extendrtests/src/rust/Cargo.toml b/tests/extendrtests/src/rust/Cargo.toml
index e20346e64..137f7bdfe 100644
--- a/tests/extendrtests/src/rust/Cargo.toml
+++ b/tests/extendrtests/src/rust/Cargo.toml
@@ -3,7 +3,7 @@ resolver = "2"
 
 [package]
 name = "extendrtests"
-version = "0.7.0"
+version = "0.7.1"
 authors = [
     "andy-thomason <andy@andythomason.com>",
     "Claus O. Wilke <wilke@austin.utexas.edu>",
@@ -15,8 +15,20 @@ publish = false
 [lib]
 crate-type = ["staticlib"]
 
+[features]
+full-functionality = []
+serde = []
+faer = []
+graphics = []
+ndarray = []
+
 [dependencies]
-extendr-api = { version = "*", features = ["graphics", "ndarray", "faer", "either"] }
+extendr-api = { version = "*", features = [
+    "graphics",
+    "ndarray",
+    "faer",
+    "either",
+] }
 faer = "0.19"
 
 [patch.crates-io]
@@ -31,4 +43,3 @@ extendr-api = { path = "../../../../extendr-api" }
 ## Build against current extendr version on github. Not recommended
 ## for development work.
 #extendr-api = { git = "https://github.com/extendr/extendr"}
-
diff --git a/tests/extendrtests/src/rust/src/externalptr.rs b/tests/extendrtests/src/rust/src/externalptr.rs
index 1d924be58..6050560f8 100644
--- a/tests/extendrtests/src/rust/src/externalptr.rs
+++ b/tests/extendrtests/src/rust/src/externalptr.rs
@@ -89,6 +89,14 @@ impl Wrapper {
     }
 }
 
+#[extendr]
+fn externalptr_use_ref_manually() -> ExternalPtr<i32> {
+    let extptr = ExternalPtr::new(1);
+    let robj: Robj = extptr.into();
+    let extptr2: &ExternalPtr<i32> = robj.try_into().unwrap();
+    extptr2.clone()
+}
+
 // Macro to generate exports
 extendr_module! {
     mod externalptr;
diff --git a/tests/extendrtests/src/rust/src/lib.rs b/tests/extendrtests/src/rust/src/lib.rs
index e1ec9d419..6d7a65e9e 100644
--- a/tests/extendrtests/src/rust/src/lib.rs
+++ b/tests/extendrtests/src/rust/src/lib.rs
@@ -12,6 +12,7 @@ mod optional_faer;
 mod optional_ndarray;
 mod raw_identifiers;
 mod submodule;
+mod to_unique_character;
 mod typedsliceargs;
 
 // Return string `"Hello world!"` to R.
@@ -356,4 +357,5 @@ extendr_module! {
     use externalptr;
     use matrix;
     use typedsliceargs;
+    use to_unique_character;
 }
diff --git a/tests/extendrtests/src/rust/src/to_unique_character.rs b/tests/extendrtests/src/rust/src/to_unique_character.rs
new file mode 100644
index 000000000..4be50d10a
--- /dev/null
+++ b/tests/extendrtests/src/rust/src/to_unique_character.rs
@@ -0,0 +1,35 @@
+use extendr_api::prelude::*;
+
+#[extendr]
+fn to_unique_rstr(r_char_vec: Strings) -> usize {
+    let capacity = r_char_vec.len();
+    let mut seen: Vec<&Rstr> = Vec::with_capacity(capacity);
+    let mut n_unique = 0;
+    for word in r_char_vec.iter() {
+        if !seen.contains(&word) {
+            n_unique += 1;
+            seen.push(word);
+        }
+    }
+    n_unique
+}
+
+#[extendr]
+fn to_unique_str(r_char_vec: StrIter) -> usize {
+    let capacity = r_char_vec.len();
+    let mut seen: Vec<&str> = Vec::with_capacity(capacity);
+    let mut n_unique = 0;
+    for word in r_char_vec {
+        if !seen.contains(&word) {
+            n_unique += 1;
+            seen.push(word);
+        }
+    }
+    n_unique
+}
+
+extendr_module! {
+    mod to_unique_character;
+    fn to_unique_rstr;
+    fn to_unique_str;
+}
diff --git a/tests/extendrtests/tests/testthat/_snaps/macro-snapshot.md b/tests/extendrtests/tests/testthat/_snaps/macro-snapshot.md
index ab832345c..41f9df03f 100644
--- a/tests/extendrtests/tests/testthat/_snaps/macro-snapshot.md
+++ b/tests/extendrtests/tests/testthat/_snaps/macro-snapshot.md
@@ -135,12 +135,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "new_usize"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -219,10 +219,10 @@
           }
           impl AltStringImpl for StringInts {
               fn elt(&self, index: usize) -> Rstr {
-                  {
-                      let res = ::alloc::fmt::format(format_args!("{0}", index));
-                      res
-                  }
+                  ::alloc::__export::must_use({
+                          let res = ::alloc::fmt::format(format_args!("{0}", index));
+                          res
+                      })
                       .into()
               }
           }
@@ -257,12 +257,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "tst_altstring"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -387,12 +387,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "tst_altinteger"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -553,12 +553,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "dbls_named"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -643,12 +643,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "strings_named"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -737,12 +737,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "list_named"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -986,7 +986,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -994,7 +994,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1077,7 +1077,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -1085,7 +1085,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1320,12 +1320,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "new"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1404,12 +1404,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "set_a"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1498,12 +1498,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "a"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1589,12 +1589,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "me_owned"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1683,12 +1683,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "me_ref"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1777,12 +1777,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "me_mut"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1871,12 +1871,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "me_explicit_ref"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -1965,12 +1965,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "me_explicit_mut"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -2067,12 +2067,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "max_ref"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -2185,12 +2185,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "max_ref_offset"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -2297,12 +2297,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "max_ref2"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -2418,6 +2418,87 @@
                       methods,
                   });
           }
+          fn externalptr_use_ref_manually() -> ExternalPtr<i32> {
+              let extptr = ExternalPtr::new(1);
+              let robj: Robj = extptr.into();
+              let extptr2: &ExternalPtr<i32> = robj.try_into().unwrap();
+              extptr2.clone()
+          }
+          #[no_mangle]
+          #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
+          pub extern "C" fn wrap__externalptr_use_ref_manually() -> extendr_api::SEXP {
+              use extendr_api::robj::*;
+              let wrap_result_state: std::result::Result<
+                  std::result::Result<extendr_api::Robj, extendr_api::Error>,
+                  Box<dyn std::any::Any + Send>,
+              > = unsafe {
+                  std::panic::catch_unwind(
+                      std::panic::AssertUnwindSafe(move || -> std::result::Result<
+                          extendr_api::Robj,
+                          extendr_api::Error,
+                      > { Ok(extendr_api::Robj::from(externalptr_use_ref_manually())) }),
+                  )
+              };
+              match wrap_result_state {
+                  Ok(Ok(zz)) => {
+                      return unsafe { zz.get() };
+                  }
+                  Ok(Err(conversion_err)) => {
+                      let err_string = conversion_err.to_string();
+                      drop(conversion_err);
+                      extendr_api::throw_r_error(&err_string);
+                  }
+                  Err(unwind_err) => {
+                      drop(unwind_err);
+                      let err_string = {
+                          let res = ::alloc::fmt::format(
+                              format_args!(
+                                  "User function panicked: {0}",
+                                  "externalptr_use_ref_manually",
+                              ),
+                          );
+                          res
+                      };
+                      extendr_api::handle_panic(
+                          err_string.as_str(),
+                          || {
+                              #[cold]
+                              #[track_caller]
+                              #[inline(never)]
+                              const fn panic_cold_explicit() -> ! {
+                                  ::core::panicking::panic_explicit()
+                              }
+                              panic_cold_explicit();
+                          },
+                      );
+                  }
+              }
+              {
+                  ::core::panicking::panic_fmt(
+                      format_args!(
+                          "internal error: entered unreachable code: {0}",
+                          format_args!("internal extendr error, this should never happen."),
+                      ),
+                  );
+              }
+          }
+          #[allow(non_snake_case)]
+          fn meta__externalptr_use_ref_manually(
+              metadata: &mut Vec<extendr_api::metadata::Func>,
+          ) {
+              let args = ::alloc::vec::Vec::new();
+              metadata
+                  .push(extendr_api::metadata::Func {
+                      doc: "",
+                      rust_name: "externalptr_use_ref_manually",
+                      r_name: "externalptr_use_ref_manually",
+                      mod_name: "externalptr_use_ref_manually",
+                      args: args,
+                      return_type: "ExternalPtr",
+                      func_ptr: wrap__externalptr_use_ref_manually as *const u8,
+                      hidden: false,
+                  })
+          }
           #[no_mangle]
           #[allow(non_snake_case)]
           pub fn get_externalptr_metadata() -> extendr_api::metadata::Metadata {
@@ -2507,19 +2588,23 @@
           impl<'a> DeviceDriver for MyDevice<'a> {
               fn activate(&mut self, _dd: DevDesc) {
                   let welcome_message = self.welcome_message;
-                  print_r_output({
-                      let res = ::alloc::fmt::format(
-                          format_args!("message from device: {0}", welcome_message),
-                      );
-                      res
-                  });
+                  print_r_output(
+                      ::alloc::__export::must_use({
+                          let res = ::alloc::fmt::format(
+                              format_args!("message from device: {0}", welcome_message),
+                          );
+                          res
+                      }),
+                  );
                   print_r_output("\n");
               }
               fn close(&mut self, _dd: DevDesc) {
-                  print_r_output({
-                      let res = ::alloc::fmt::format(format_args!("good bye..."));
-                      res
-                  });
+                  print_r_output(
+                      ::alloc::__export::must_use({
+                          let res = ::alloc::fmt::format(format_args!("good bye..."));
+                          res
+                      }),
+                  );
                   print_r_output("\n");
               }
           }
@@ -2558,12 +2643,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "fetch_dimnames"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -2643,12 +2728,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "fetch_rownames"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -2728,12 +2813,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "fetch_colnames"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -2819,12 +2904,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "change_dimnames"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3001,7 +3086,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -3009,7 +3094,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3107,7 +3192,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -3115,7 +3200,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3205,7 +3290,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -3213,7 +3298,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3295,7 +3380,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -3303,7 +3388,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3389,7 +3474,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -3397,7 +3482,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3483,7 +3568,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -3491,7 +3576,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3664,12 +3749,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "type_aware_sum"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3835,12 +3920,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "mat_to_mat"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -3920,12 +4005,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "mat_to_rmat"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4005,12 +4090,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "mat_to_robj"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4090,12 +4175,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "mat_to_rmatfloat"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4175,12 +4260,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "rmat_to_mat"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4260,12 +4345,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "robj_to_mat"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4345,12 +4430,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "matref_to_mat"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4619,12 +4704,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "euclidean_dist"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4799,7 +4884,7 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!(
                                   "User function panicked: {0}",
@@ -4807,7 +4892,7 @@
                               ),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4887,12 +4972,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "r#true"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -4967,12 +5052,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "r#false"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -5138,12 +5223,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "hello_submodule"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -5264,12 +5349,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "new"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -5348,12 +5433,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "set_a"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -5444,12 +5529,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "a"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -5634,6 +5719,298 @@
               unsafe { extendr_api::register_call_methods(info, get_submodule_metadata()) };
           }
       }
+      mod to_unique_character {
+          use extendr_api::prelude::*;
+          fn to_unique_rstr(r_char_vec: Strings) -> usize {
+              let capacity = r_char_vec.len();
+              let mut seen: Vec<&Rstr> = Vec::with_capacity(capacity);
+              let mut n_unique = 0;
+              for word in r_char_vec.iter() {
+                  if !seen.contains(&word) {
+                      n_unique += 1;
+                      seen.push(word);
+                  }
+              }
+              n_unique
+          }
+          #[no_mangle]
+          #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
+          pub extern "C" fn wrap__to_unique_rstr(
+              r_char_vec: extendr_api::SEXP,
+          ) -> extendr_api::SEXP {
+              use extendr_api::robj::*;
+              let wrap_result_state: std::result::Result<
+                  std::result::Result<extendr_api::Robj, extendr_api::Error>,
+                  Box<dyn std::any::Any + Send>,
+              > = unsafe {
+                  std::panic::catch_unwind(
+                      std::panic::AssertUnwindSafe(move || -> std::result::Result<
+                          extendr_api::Robj,
+                          extendr_api::Error,
+                      > {
+                          let _r_char_vec_robj = extendr_api::robj::Robj::from_sexp(
+                              r_char_vec,
+                          );
+                          Ok(
+                              extendr_api::Robj::from(
+                                  to_unique_rstr(_r_char_vec_robj.try_into()?),
+                              ),
+                          )
+                      }),
+                  )
+              };
+              match wrap_result_state {
+                  Ok(Ok(zz)) => {
+                      return unsafe { zz.get() };
+                  }
+                  Ok(Err(conversion_err)) => {
+                      let err_string = conversion_err.to_string();
+                      drop(conversion_err);
+                      extendr_api::throw_r_error(&err_string);
+                  }
+                  Err(unwind_err) => {
+                      drop(unwind_err);
+                      let err_string = ::alloc::__export::must_use({
+                          let res = ::alloc::fmt::format(
+                              format_args!("User function panicked: {0}", "to_unique_rstr"),
+                          );
+                          res
+                      });
+                      extendr_api::handle_panic(
+                          err_string.as_str(),
+                          || {
+                              #[cold]
+                              #[track_caller]
+                              #[inline(never)]
+                              const fn panic_cold_explicit() -> ! {
+                                  ::core::panicking::panic_explicit()
+                              }
+                              panic_cold_explicit();
+                          },
+                      );
+                  }
+              }
+              {
+                  ::core::panicking::panic_fmt(
+                      format_args!(
+                          "internal error: entered unreachable code: {0}",
+                          format_args!("internal extendr error, this should never happen."),
+                      ),
+                  );
+              }
+          }
+          #[allow(non_snake_case)]
+          fn meta__to_unique_rstr(metadata: &mut Vec<extendr_api::metadata::Func>) {
+              let args = <[_]>::into_vec(
+                  #[rustc_box]
+                  ::alloc::boxed::Box::new([
+                      extendr_api::metadata::Arg {
+                          name: "r_char_vec",
+                          arg_type: "Strings",
+                          default: None,
+                      },
+                  ]),
+              );
+              metadata
+                  .push(extendr_api::metadata::Func {
+                      doc: "",
+                      rust_name: "to_unique_rstr",
+                      r_name: "to_unique_rstr",
+                      mod_name: "to_unique_rstr",
+                      args: args,
+                      return_type: "usize",
+                      func_ptr: wrap__to_unique_rstr as *const u8,
+                      hidden: false,
+                  })
+          }
+          fn to_unique_str(r_char_vec: StrIter) -> usize {
+              let capacity = r_char_vec.len();
+              let mut seen: Vec<&str> = Vec::with_capacity(capacity);
+              let mut n_unique = 0;
+              for word in r_char_vec {
+                  if !seen.contains(&word) {
+                      n_unique += 1;
+                      seen.push(word);
+                  }
+              }
+              n_unique
+          }
+          #[no_mangle]
+          #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
+          pub extern "C" fn wrap__to_unique_str(
+              r_char_vec: extendr_api::SEXP,
+          ) -> extendr_api::SEXP {
+              use extendr_api::robj::*;
+              let wrap_result_state: std::result::Result<
+                  std::result::Result<extendr_api::Robj, extendr_api::Error>,
+                  Box<dyn std::any::Any + Send>,
+              > = unsafe {
+                  std::panic::catch_unwind(
+                      std::panic::AssertUnwindSafe(move || -> std::result::Result<
+                          extendr_api::Robj,
+                          extendr_api::Error,
+                      > {
+                          let _r_char_vec_robj = extendr_api::robj::Robj::from_sexp(
+                              r_char_vec,
+                          );
+                          Ok(
+                              extendr_api::Robj::from(
+                                  to_unique_str(_r_char_vec_robj.try_into()?),
+                              ),
+                          )
+                      }),
+                  )
+              };
+              match wrap_result_state {
+                  Ok(Ok(zz)) => {
+                      return unsafe { zz.get() };
+                  }
+                  Ok(Err(conversion_err)) => {
+                      let err_string = conversion_err.to_string();
+                      drop(conversion_err);
+                      extendr_api::throw_r_error(&err_string);
+                  }
+                  Err(unwind_err) => {
+                      drop(unwind_err);
+                      let err_string = ::alloc::__export::must_use({
+                          let res = ::alloc::fmt::format(
+                              format_args!("User function panicked: {0}", "to_unique_str"),
+                          );
+                          res
+                      });
+                      extendr_api::handle_panic(
+                          err_string.as_str(),
+                          || {
+                              #[cold]
+                              #[track_caller]
+                              #[inline(never)]
+                              const fn panic_cold_explicit() -> ! {
+                                  ::core::panicking::panic_explicit()
+                              }
+                              panic_cold_explicit();
+                          },
+                      );
+                  }
+              }
+              {
+                  ::core::panicking::panic_fmt(
+                      format_args!(
+                          "internal error: entered unreachable code: {0}",
+                          format_args!("internal extendr error, this should never happen."),
+                      ),
+                  );
+              }
+          }
+          #[allow(non_snake_case)]
+          fn meta__to_unique_str(metadata: &mut Vec<extendr_api::metadata::Func>) {
+              let args = <[_]>::into_vec(
+                  #[rustc_box]
+                  ::alloc::boxed::Box::new([
+                      extendr_api::metadata::Arg {
+                          name: "r_char_vec",
+                          arg_type: "StrIter",
+                          default: None,
+                      },
+                  ]),
+              );
+              metadata
+                  .push(extendr_api::metadata::Func {
+                      doc: "",
+                      rust_name: "to_unique_str",
+                      r_name: "to_unique_str",
+                      mod_name: "to_unique_str",
+                      args: args,
+                      return_type: "usize",
+                      func_ptr: wrap__to_unique_str as *const u8,
+                      hidden: false,
+                  })
+          }
+          #[no_mangle]
+          #[allow(non_snake_case)]
+          pub fn get_to_unique_character_metadata() -> extendr_api::metadata::Metadata {
+              let mut functions = Vec::new();
+              let mut impls = Vec::new();
+              meta__to_unique_rstr(&mut functions);
+              meta__to_unique_str(&mut functions);
+              functions
+                  .push(extendr_api::metadata::Func {
+                      doc: "Metadata access function.",
+                      rust_name: "get_to_unique_character_metadata",
+                      mod_name: "get_to_unique_character_metadata",
+                      r_name: "get_to_unique_character_metadata",
+                      args: Vec::new(),
+                      return_type: "Metadata",
+                      func_ptr: wrap__get_to_unique_character_metadata as *const u8,
+                      hidden: true,
+                  });
+              functions
+                  .push(extendr_api::metadata::Func {
+                      doc: "Wrapper generator.",
+                      rust_name: "make_to_unique_character_wrappers",
+                      mod_name: "make_to_unique_character_wrappers",
+                      r_name: "make_to_unique_character_wrappers",
+                      args: <[_]>::into_vec(
+                          #[rustc_box]
+                          ::alloc::boxed::Box::new([
+                              extendr_api::metadata::Arg {
+                                  name: "use_symbols",
+                                  arg_type: "bool",
+                                  default: None,
+                              },
+                              extendr_api::metadata::Arg {
+                                  name: "package_name",
+                                  arg_type: "&str",
+                                  default: None,
+                              },
+                          ]),
+                      ),
+                      return_type: "String",
+                      func_ptr: wrap__make_to_unique_character_wrappers as *const u8,
+                      hidden: true,
+                  });
+              extendr_api::metadata::Metadata {
+                  name: "to_unique_character",
+                  functions,
+                  impls,
+              }
+          }
+          #[no_mangle]
+          #[allow(non_snake_case)]
+          pub extern "C" fn wrap__get_to_unique_character_metadata() -> extendr_api::SEXP {
+              use extendr_api::GetSexp;
+              unsafe { extendr_api::Robj::from(get_to_unique_character_metadata()).get() }
+          }
+          #[no_mangle]
+          #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
+          pub extern "C" fn wrap__make_to_unique_character_wrappers(
+              use_symbols_sexp: extendr_api::SEXP,
+              package_name_sexp: extendr_api::SEXP,
+          ) -> extendr_api::SEXP {
+              unsafe {
+                  use extendr_api::robj::*;
+                  use extendr_api::GetSexp;
+                  let robj = Robj::from_sexp(use_symbols_sexp);
+                  let use_symbols: bool = <bool>::try_from(&robj).unwrap();
+                  let robj = Robj::from_sexp(package_name_sexp);
+                  let package_name: &str = <&str>::try_from(&robj).unwrap();
+                  extendr_api::Robj::from(
+                          get_to_unique_character_metadata()
+                              .make_r_wrappers(use_symbols, package_name)
+                              .unwrap(),
+                      )
+                      .get()
+              }
+          }
+          #[no_mangle]
+          #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
+          pub extern "C" fn R_init_to_unique_character_extendr(
+              info: *mut extendr_api::DllInfo,
+          ) {
+              unsafe {
+                  extendr_api::register_call_methods(info, get_to_unique_character_metadata())
+              };
+          }
+      }
       mod typedsliceargs {
           use extendr_api::prelude::*;
           fn middle_zero(integers: &mut [Rint]) {
@@ -5671,12 +6048,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "middle_zero"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -5766,12 +6143,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "logicals_sum"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -5865,12 +6242,12 @@
                   }
                   Err(unwind_err) => {
                       drop(unwind_err);
-                      let err_string = {
+                      let err_string = ::alloc::__export::must_use({
                           let res = ::alloc::fmt::format(
                               format_args!("User function panicked: {0}", "floats_mean"),
                           );
                           res
-                      };
+                      });
                       extendr_api::handle_panic(
                           err_string.as_str(),
                           || {
@@ -6032,12 +6409,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "hello_world"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6103,12 +6480,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "do_nothing"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6179,12 +6556,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "double_scalar"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6264,12 +6641,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "int_scalar"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6349,12 +6726,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "bool_scalar"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6434,12 +6811,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "char_scalar"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6519,12 +6896,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "char_vec"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6604,12 +6981,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "double_vec"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6686,12 +7063,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "try_rfloat_na"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6759,12 +7136,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "try_rint_na"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6835,12 +7212,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "check_rfloat_na"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -6920,12 +7297,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "check_rint_na"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7013,12 +7390,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "get_doubles_element"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7111,12 +7488,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "get_integers_element"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7209,12 +7586,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "get_logicals_element"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7303,12 +7680,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "doubles_square"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7392,12 +7769,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "complexes_square"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7481,12 +7858,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "integers_square"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7570,12 +7947,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "logicals_not"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7655,12 +8032,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "check_default"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7753,12 +8130,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "special_param_names"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7842,7 +8219,7 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!(
                               "User function panicked: {0}",
@@ -7850,7 +8227,7 @@
                           ),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7918,12 +8295,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "test.rename.rlike"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -7994,12 +8371,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "get_default_value"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8079,12 +8456,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "add_5_if_not_null"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8210,12 +8587,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "new"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8294,12 +8671,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "set_a"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8388,12 +8765,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "a"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8480,12 +8857,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "me"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8568,12 +8945,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "restore_from_robj"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8656,12 +9033,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "get_default_value"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8816,12 +9193,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "new"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -8898,12 +9275,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "__name_test"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -9066,12 +9443,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "new"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -9148,12 +9525,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "a"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -9300,12 +9677,12 @@
               }
               Err(unwind_err) => {
                   drop(unwind_err);
-                  let err_string = {
+                  let err_string = ::alloc::__export::must_use({
                       let res = ::alloc::fmt::format(
                           format_args!("User function panicked: {0}", "my_device"),
                       );
                       res
-                  };
+                  });
                   extendr_api::handle_panic(
                       err_string.as_str(),
                       || {
@@ -9399,6 +9776,7 @@
           functions.extend(externalptr::get_externalptr_metadata().functions);
           functions.extend(matrix::get_matrix_metadata().functions);
           functions.extend(typedsliceargs::get_typedsliceargs_metadata().functions);
+          functions.extend(to_unique_character::get_to_unique_character_metadata().functions);
           impls.extend(altrep::get_altrep_metadata().impls);
           impls.extend(attributes::get_attributes_metadata().impls);
           impls.extend(dataframe::get_dataframe_metadata().impls);
@@ -9411,6 +9789,7 @@
           impls.extend(externalptr::get_externalptr_metadata().impls);
           impls.extend(matrix::get_matrix_metadata().impls);
           impls.extend(typedsliceargs::get_typedsliceargs_metadata().impls);
+          impls.extend(to_unique_character::get_to_unique_character_metadata().impls);
           functions
               .push(extendr_api::metadata::Func {
                   doc: "Metadata access function.",