From 083ca7786fd0e547f851c0b4eaec68fad7effa10 Mon Sep 17 00:00:00 2001 From: Mossa Date: Sun, 30 Jun 2024 20:41:15 +0200 Subject: [PATCH] standalone: update to extendr 0.7.0 --- _freeze/changelog/execute-results/html.json | 2 +- .../complete-example/execute-results/html.json | 2 +- .../extendr-macro/execute-results/html.json | 4 ++-- .../scalars/execute-results/html.json | 4 ++-- .../vectors/execute-results/html.json | 2 +- user-guide/_drafts/conversion.qmd | 17 ++++++++--------- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/_freeze/changelog/execute-results/html.json b/_freeze/changelog/execute-results/html.json index 97c496b..c3db5f6 100644 --- a/_freeze/changelog/execute-results/html.json +++ b/_freeze/changelog/execute-results/html.json @@ -2,7 +2,7 @@ "hash": "62286ea7d715c19174e66c7ae8ef8865", "result": { "engine": "knitr", - "markdown": "# Changelog\n\n## Unreleased\n\n### Added\n\n- Arguments can be (mutable) typed slices such as `&[Rbool]`, `&mut [Rint]` etc. [[#790]](https://github.com/extendr/extendr/pull/790)\n- New optional `faer` feature which enables conversion between `faer` matrix and `RMatrix` [[#706]](https://github.com/extendr/extendr/pull/706)\n- Adds `TryFrom` and `` for `impl` blocks marked with `#[extendr]` macro allowing falliable conversion to `&Self` `&mut Self`\n- Adds `From for Robj` for impl blocks marked with `#[extendr]` macro\n- The new `ExpectedExternalNonNullPtr` error variant provides a more informative error when a null pointer is accessed\n- `RArray::data_mut` provides a mutable slice to the underlying array data [[#657]](https://github.com/extendr/extendr/pull/657)\n- Implements the `Attributes` trait for all R vector wrapper structs (e.g. `Integers` , `Doubles`, `Strings`, etc.), allowing for easy access and setting of the attributes of an R object [[#745]](https://github.com/extendr/extendr/pull/745). This comes with breaking changes. See below.\n- feature `non-api` that gives access to non-API items; Requires compile-time generation of bindings\n[[#754]](https://github.com/extendr/extendr/pull/754)\n- `TryFrom<&Robj>` for `StrIter`, `HashMap` for `K = String` and `K = &str` [[#759]](https://github.com/extendr/extendr/pull/759)\n\n### Changed\n\n- [_Potentially breaking_]: `RArray::from_parts` no longer requires a pointer to the underlying data\n vector [[#657]](https://github.com/extendr/extendr/pull/657)\n- `#[extendr(use_try_from = true)` is now the default setting, therefore the option `use_try_from` has been removed [[#759]](https://github.com/extendr/extendr/pull/759)\n\n#### Breaking changes\n\n- R-devel Non-API changes:\n - R's C API is being formalized. While the changes are formalized, non-API functions are hidden behind a feature flag to prevent removal from CRAN.\n - Non-API [changes are in flux in R-devel](https://github.com/r-devel/r-svn/blob/71afe1e304b11f7febaa536e96817c63a7c1c7ab/src/library/tools/R/sotools.R#L564), however, CRAN has set a July 9th date to remove any package that uses non-API functions. This includes almost every extendr based package on CRAN.\n - See [[Rd] clarifying and adjusting the C API for R](https://stat.ethz.ch/pipermail/r-devel/2024-June/083449.html)\n - [nonAPI.txt](https://github.com/r-devel/r-svn/blob/f36c203d3a53a74d56a81d4f97a68d24993e0652/src/library/tools/R/sotools.R#L564) functions are hidden behind the `non-api` feature flag.\n - Removed from default include (but may not be limited to):\n - `global_var()`, `local_var()`, `base_env()`, various `Environment`, `Function`, `Primitive`, and `Promise` methods.\n- `Attributes` trait now returns a mutable reference\n to `Self`. [[#745]](https://github.com/extendr/extendr/pull/745). Previously `.set_attrib()` would modify an object in\n place, and then return an untyped owned pointer (Robj). Instead, now we return `&mut Self`.\n- In `AltRep` the `unserialize_ex`, `set_parent`, `set_envflags` are\nnow hidden behind the feature flag `non-api`. Also, `Promise::from_parts` is marked as non-API.\n- Floating point numbers with decimal part can no longer be converted to integer types via\n rounding [[#757]](https://github.com/extendr/extendr/pull/757)\n- You can no longer create an `Robj` from a reference `&T`, where `T` is an `extendr`-impl. [[#759]](https://github.com/extendr/extendr/pull/759)\n- You can no longer use `from_robj`, as the trait `FromRobj` as been removed. Instead, use `try_from`.\n- It is no longer possible to access an R integer vector as a `&[u32]` [[#767]](https://github.com/extendr/extendr/pull/767)\n\n### Fixed\n\n- returning `&Self` or `&mut Self` from a method in an `#[extendr]`-impl would\nresult in unintended cloning [[#614]](https://github.com/extendr/extendr/issues/614)\n- `TryFrom<&Robj>` and `FromRobj` for integer scalars now correctly handles conversions\n from `f64` [[#757]](https://github.com/extendr/extendr/pull/757)\n\n## 0.6.0\n\n### Added\n\n- `ALTLIST` support allowing users to represent structs as R list objects\n- [**either**] `TryFrom<&Robj> for Either` and `From> for Robj` if `T` and `R` are themselves implement these traits. This unblocks scenarios like accepting any numeric vector from R via `Either` without extra memory allocation [[#480]](https://github.com/extendr/extendr/pull/480)\n- `PartialOrd` trait implementation for `Rfloat`, `Rint` and `Rbool`. `Rfloat` and `Rint` gained `min()` and `max()` methods [[#573]](https://github.com/extendr/extendr/pull/573)\n- `use_rng` option for the `extendr` attribute macro, which enables the use of\nrandom number sampling methods from R, e.g. `#[extendr(use_rng = true)` [[#476]](https://github.com/extendr/extendr/pull/476)\n- `[T; N]` conversions to `Robj` [[#594]](https://github.com/extendr/extendr/pull/594/)\n- `ToVectorValue` for `Rfloat`, `Rint` and `Rbool` [[#593]](https://github.com/extendr/extendr/pull/593)\n- `TryFrom<_>` on `Vec<_>` for `Integers` (`i32`), `Complexes` (`c64`), `Doubles` (`f64`), and `Logicals` (`bool` / `i32`). [[#593]](https://github.com/extendr/extendr/pull/593)\n- `Rstr` can now be constructed from `Option` [[#630]](https://github.com/extendr/extendr/pull/630)\n\n### Fixed\n\n- You can now create `ArrayView1` from `&Robj` as well as `Robj`\n [[#501]](https://github.com/extendr/extendr/pull/501)\n- Raw literals from Rust can be used for function and argument names. e.g.\n `fn r#type()` in Rust is converted to `type()` in R.\n [[#531]](https://github.com/extendr/extendr/pull/531)\n- Fix memory leaks on errors and panics\n [[#555]](https://github.com/extendr/extendr/pull/555)\n- Fixed error when collecting too many objects into `List`, etc.\n [[#540]](https://github.com/extendr/extendr/pull/540)\n\n## 0.4.0\n\n### Added\n\n- Support for setting the default value of arguments to struct methods, using `#[default = \"...\"]` [[#436]](https://github.com/extendr/extendr/pull/436)\n- [**ndarray**] `TryFrom<&Robj>` for `ArrayView1` and `ArrayView2`, where `T` is `i32`, `f64`, `c64`, `Rint`, `Rfloat`, `Rcplx`, `Rstr`, `Rbool` [[#443]](https://github.com/extendr/extendr/pull/443)\n- `Debug` trait implementation for `Rcplx` and `Complexes` [[#444]](https://github.com/extendr/extendr/pull/444)\n- `TryFrom`, `From>`, `Into>` and their variations for `Nullable` [[#446]](https://github.com/extendr/extendr/pull/446)\n- `Nullable::map()` that acts on not null value and propagates `NULL` [[#446]](https://github.com/extendr/extendr/pull/446)\n- [**ndarray**] Conversion from owned arrays (ie `ndarray::Array`) into `Robj` [[#450]](https://github.com/extendr/extendr/pull/450)\n- [**ndarray**][**docs**] Documentation for the `robj_ndarray` module [[#450]](https://github.com/extendr/extendr/pull/450)\n- `Sum` for scalars like `Rint`, `Rfloat` and `Rcplx`, which accept `Iterator` [[#454]](https://github.com/extendr/extendr/pull/454)\n- A new `collect_rarray` method that can be used to collect arbitrary iterables into an R matrix\n [[#466]](https://github.com/extendr/extendr/pull/466)\n- [**docs**] Documentation for `RArray::new_matrix()` [[#466]](https://github.com/extendr/extendr/pull/466)\n\n### Changed\n\n- [**docs**] Use bindgen on docs.rs, to ensure newer R features will still be documented [[#426]](https://github.com/extendr/extendr/pull/426)\n- Unify the tagging mechanism used to identify Rust types inside `ExternalPtr`. This allows `#[extendr]`-annotated functions to directly accept `ExternalPtr` as well as `MyStruct` [[#433]](https://github.com/extendr/extendr/pull/433)\n- `Nullable` is now part of `extendr_api::prelude` [[#446]](https://github.com/extendr/extendr/pull/446)\n- Bump the Rust edition from 2018 to 2021 [[#458]](https://github.com/extendr/extendr/pull/458)\n- When converted to `STRSXP`, strings are now correctly marked as UTF-8 even on non-UTF-8 platforms (i.e., R < 4.2 on Windows), which shouldn't matter for most of the users [[#467]](https://github.com/extendr/extendr/pull/467)\n\n### Fixed\n\n- The R CMD check note \"Found non-API calls to R\" by moving `use extendr_engine;` inside `test!` macro [[#424]](https://github.com/extendr/extendr/pull/424)\n- The clippy lint \"this public function might dereference a raw pointer but is not marked `unsafe`\" [[#451]](https://github.com/extendr/extendr/pull/451)\n- A bug where importing a submodule via `use some_module;` inside the `extendr_module!` macro wasn't working [[#469]](https://github.com/extendr/extendr/pull/469)\n\n## 0.3.0\n\n### Added\n\n- `Function` type that wraps an R function, which can be invoked using the `call()` method. [[#188]](https://github.com/extendr/extendr/pull/188)\n- `pairlist!` macro for generating `Pairlist` objects, e.g. for use in function calls. [[#202]](https://github.com/extendr/extendr/pull/202)\n- `use_try_from` option for the `extendr` macro, which allows the use of any type that implements `TryInto`/`TryFrom`, e.g. `#[extendr(use_try_from = true)]`. [[#222]](https://github.com/extendr/extendr/pull/222)\n- Support for R version 4.2. [[#235]](https://github.com/extendr/extendr/issues/235)\n- `call!` macro, which can be used to call an R function whose name is provided as a string. [[#238]](https://github.com/extendr/extendr/pull/238)\n- Large Rust integer types (`u32`, `u64` and `i64`) can now be converted to R's `numeric` type, which can handle large integer values. [[#242]](https://github.com/extendr/extendr/pull/242)\n- `TryFrom` for a large number of Rust types. [[#249]](https://github.com/extendr/extendr/pull/249), [[#258]](https://github.com/extendr/extendr/pull/258)\n- Support for `ALTREP`. [[#250]](https://github.com/extendr/extendr/pull/250), [[#274]](https://github.com/extendr/extendr/pull/274)\n- `S4` struct, which wraps an S4 class in R. [[#268]](https://github.com/extendr/extendr/pull/268)\n- [**ndarray**] Implemented `TryFrom<&ArrayBase> for Robj`, allowing `extendr`-annotated functions to return Arrays from the `ndarray` crate and have them automatically converted to R arrays. [[#275]](https://github.com/extendr/extendr/pull/275)\n- `Rint`, `Rdouble`, `Rbool` and `Rcplx`: `NA`-aware wrappers for scalar elements of R vectors [[#274]](https://github.com/extendr/extendr/pull/274), [[#284]](https://github.com/extendr/extendr/pull/284), [[#301]](https://github.com/extendr/extendr/pull/301), [[#338]](https://github.com/extendr/extendr/pull/338), [[#350]](https://github.com/extendr/extendr/pull/350)\n- `Integers`, `Doubles`, `Strings`, `Logicals` and `Complexes`: wrappers for R vectors that deref to slices of the above types (`RInt` etc). [[#274]](https://github.com/extendr/extendr/pull/274), [[#284]](https://github.com/extendr/extendr/pull/284), [[#301]](https://github.com/extendr/extendr/pull/301), [[#338]](https://github.com/extendr/extendr/pull/338), [[#350]](https://github.com/extendr/extendr/pull/350)\n- `ExternalPtr`, a wrapper class for creating R objects containing any Rust object. [[#260]](https://github.com/extendr/extendr/pull/260)\n- [**graphics**] Support for R graphics and graphics devices. The `graphics` feature flag is disabled by default. [[#279]](https://github.com/extendr/extendr/pull/279), [[#360]](https://github.com/extendr/extendr/pull/360), [[#373]](https://github.com/extendr/extendr/pull/373), [[#379]](https://github.com/extendr/extendr/pull/379), [[#380]](https://github.com/extendr/extendr/pull/380), [[#389]](https://github.com/extendr/extendr/pull/389)\n- `Deref` implementation for vector types (Rint/Rfloat/Rbool/Rstr/Robj) to appropriately typed Rust slices. [[#327]](https://github.com/extendr/extendr/pull/327)\n- `default` option for `extendr`-annotated functions, allowing them to have default values, e.g. `fn fred(#[default=\"NULL\"] x: Option) { }`. [[#334]](https://github.com/extendr/extendr/pull/334)\n- `r_name` option for `extendr`-annotated functions, allowing the generated R function to have a different name. e.g.\n\n ```rust\n #[extendr(\n use_try_from = true,\n r_name = \"test.rename.rlike\",\n mod_name = \"test_rename_mymod\"\n )]\n fn test_rename() { }\n ```\n\n [[#335]](https://github.com/extendr/extendr/pull/335)\n- `serde::Serialize` implementation for R types. [[#305]](https://github.com/extendr/extendr/pull/305), [[#355]](https://github.com/extendr/extendr/pull/355)\n- `Rany` type and the `as_any` conversion method. [[#320]](https://github.com/extendr/extendr/pull/320)\n- `std::fmt::Debug` implementation for wrapper types. [[#345]](https://github.com/extendr/extendr/pull/345)\n- `#[derive(TryFromRobj)` and `#[derive(IntoRobj)]` which provide an automatic conversion from and to any custom Rust struct and `Robj` [[#347]](https://github.com/extendr/extendr/pull/347)\n- `[[` operator that works with Rust classes. Its behavior is identical to that of the `$` operator. [[#359]](https://github.com/extendr/extendr/pull/359)\n- `Load` and `Save`, traits that, once implemented, provide the ability to load and save R data in the RDS format. These traits are implemented for all `Robj`. [[#363]](https://github.com/extendr/extendr/pull/363)\n- `Dataframe` wrapper struct. [[#393]](https://github.com/extendr/extendr/pull/393)\n- `IntoDataFrame` trait, which can be derived to allow arbitrary Rust structs to be converted to rows of a data frame. [[#393]](https://github.com/extendr/extendr/pull/393)\n\n### Changed\n\n- `Strings::elt` now returns an `Rstr`. [[#345]](https://github.com/extendr/extendr/pull/345)\n- Renamed `RType` to `Rtype`. [[#345]](https://github.com/extendr/extendr/pull/345)\n- Wrapper types now contain `Robj` fields. [[#190]](https://github.com/extendr/extendr/pull/190)\n- The `R!` macro now accepts strings that contain R code. This is now the recommended way of using the macro, especially with raw strings e.g.\n\n ```rust\n R!(r#\"\n print(\"hello\")\n \"#);\n ```\n\n [[#203]](https://github.com/extendr/extendr/pull/203)\n- Improved error handling for `<&str>::try_from(Robj)`. [[#226]](https://github.com/extendr/extendr/pull/226)\n- `SymPair::sym_pair()` now returns `(Option, Robj)`. [[#225]](https://github.com/extendr/extendr/pull/225)\n- More detailed error messages when converting Rust integer types to R. [[#243]](https://github.com/extendr/extendr/pull/243)\n- `Character` is now called `Rstr`. [[#273]](https://github.com/extendr/extendr/pull/273)\n- [**ndarray**] Bumped `ndarray` to 0.15.3. Under [RFC 1977](https://github.com/rust-lang/rfcs/pull/1977) this is a \"public dependency\" change, and therefore can be considered a breaking change, as consumers of `extendr` that use an older version of `ndarray` will no longer be compatible until they also bump `ndarray` to a compatible version. [[#275]](https://github.com/extendr/extendr/pull/275)\n- `IsNA` trait has been renamed to `CanBeNA`. [[#288]](https://github.com/extendr/extendr/pull/288)\n- `list!` has been rewritten, and now returns a `List` struct. [[#303]](https://github.com/extendr/extendr/pull/303)\n\n### Deprecated\n\n- Calling the `R!` macro with non-string types (e.g. `R!(1)`) is now deprecated. [[#203]](https://github.com/extendr/extendr/pull/203)\n\n### Removed\n\n- `Real`, `Int`, `Bool` and the redundant trait `SliceIter`, which should be replaced with `Rdouble`, `Rint`, and `Rbool` respectively. [[#304]](https://github.com/extendr/extendr/pull/304), [[#338]](https://github.com/extendr/extendr/pull/338)\n- `TryFrom` conversions between `Robj` and `HashMap` for consistency. `List::into_hashmap()` and `List::from_hashmap()` should be used instead. [[#254]](https://github.com/extendr/extendr/pull/254)\n\n## extendr 0.2.0\n\n- Added contributing guidelines and code of conduct.\n\n- Made use of ndarray optional.\n\n- Made #[extendr] calls panic and thread safe.\n\n- Added NA handling to the #[extendr] macro.\n\n- Added a separate extendr-engine crate that is needed when calling R from Rust.\n\n- Wrapper classes for pairlists, environment, raw, symbols and others.\n\n- More iterator support.\n\n- Operators index, slice, dollar, double_colon, +, -, * and /`.\n\n- Debug printing support expanded to use wrappers.\n\n- Conversion of Robj to wrapper types.\n\n- Multithreaded support - allows multithreaded testing using a recursive spinlock.\n\n- Bool type extended and implemented using TRUE, FALSE and NA_BOOLEAN.\n\n- Optional parameters to support NA handing.\n\n- Errors thrown if input parameters without Option are NA.\n\n- Harmonising of function names into integer, real, logical, symbol, raw, list, pairlist and env.\n\n- Refactored robj code into several source files.\n\n- Many functions updated to use generic types.\n\n- R! macro for executing R source code.\n\n- call! macro to call R code.\n\n- sym! macro to generate symbols.\n\n- Simplification of vector generation using collect_robj and ToVectorValue.\n\n- Added array types `[1, 2, 3]` as `Robj::from` targets.\n\n- Macros now mostly return errors.\n\n## extendr 0.1.10\n\n- Fix build on Windows and MacOS.\n", + "markdown": "# Changelog\n\n## Unreleased\n\n### Added\n\n- Arguments can be (mutable) typed slices such as `&[Rbool]`, `&mut [Rint]` etc. [[#790]](https://github.com/extendr/extendr/pull/790)\n- New optional `faer` feature which enables conversion between `faer` matrix and `RMatrix` [[#706]](https://github.com/extendr/extendr/pull/706)\n- Adds `TryFrom` and `` for `impl` blocks marked with `#[extendr]` macro allowing falliable conversion to `&Self` `&mut Self`\n- Adds `From for Robj` for impl blocks marked with `#[extendr]` macro\n- The new `ExpectedExternalNonNullPtr` error variant provides a more informative error when a null pointer is accessed\n- `RArray::data_mut` provides a mutable slice to the underlying array data [[#657]](https://github.com/extendr/extendr/pull/657)\n- Implements the `Attributes` trait for all R vector wrapper structs (e.g. `Integers` , `Doubles`, `Strings`, etc.), allowing for easy access and setting of the attributes of an R object [[#745]](https://github.com/extendr/extendr/pull/745). This comes with breaking changes. See below.\n- feature `non-api` that gives access to non-API items; Requires compile-time generation of bindings\n[[#754]](https://github.com/extendr/extendr/pull/754)\n- `TryFrom<&Robj>` for `StrIter`, `HashMap` for `K = String` and `K = &str` [[#759]](https://github.com/extendr/extendr/pull/759)\n\n### Changed\n\n- [_Potentially breaking_]: `RArray::from_parts` no longer requires a pointer to the underlying data\n vector [[#657]](https://github.com/extendr/extendr/pull/657)\n- `#[extendr(use_try_from = true)` is now the default setting, therefore the option `use_try_from` has been removed [[#759]](https://github.com/extendr/extendr/pull/759)\n\n#### Breaking changes\n\n- R-devel Non-API changes:\n - R's C API is being formalized. While the changes are formalized, non-API functions are hidden behind a feature flag to prevent removal from CRAN.\n - Non-API [changes are in flux in R-devel](https://github.com/r-devel/r-svn/blob/71afe1e304b11f7febaa536e96817c63a7c1c7ab/src/library/tools/R/sotools.R#L564), however, CRAN has set a July 9th date to remove any package that uses non-API functions. This includes almost every extendr based package on CRAN.\n - See [[Rd] clarifying and adjusting the C API for R](https://stat.ethz.ch/pipermail/r-devel/2024-June/083449.html)\n - [nonAPI.txt](https://github.com/r-devel/r-svn/blob/f36c203d3a53a74d56a81d4f97a68d24993e0652/src/library/tools/R/sotools.R#L564) functions are hidden behind the `non-api` feature flag.\n - Removed from default include (but may not be limited to):\n - `global_var()`, `local_var()`, `base_env()`, various `Environment`, `Function`, `Primitive`, and `Promise` methods.\n- `Attributes` trait now returns a mutable reference\n to `Self`. [[#745]](https://github.com/extendr/extendr/pull/745). Previously `.set_attrib()` would modify an object in\n place, and then return an untyped owned pointer (Robj). Instead, now we return `&mut Self`.\n- In `AltRep` the `unserialize_ex`, `set_parent`, `set_envflags` are\nnow hidden behind the feature flag `non-api`. Also, `Promise::from_parts` is marked as non-API.\n- Floating point numbers with decimal part can no longer be converted to integer types via\n rounding [[#757]](https://github.com/extendr/extendr/pull/757)\n- You can no longer create an `Robj` from a reference `&T`, where `T` is an `extendr`-impl. [[#759]](https://github.com/extendr/extendr/pull/759)\n- You can no longer use `from_robj`, as the trait `FromRobj` as been removed. Instead, use `try_from`.\n- It is no longer possible to access an R integer vector as a `&[u32]` [[#767]](https://github.com/extendr/extendr/pull/767)\n- It is no longer possible to generate bindings as part of the compilation of\nextendr. Feature `non-api` is broken and will not compile. Related \n\n### Fixed\n\n- returning `&Self` or `&mut Self` from a method in an `#[extendr]`-impl would\nresult in unintended cloning [[#614]](https://github.com/extendr/extendr/issues/614)\n- `TryFrom<&Robj>` and `FromRobj` for integer scalars now correctly handles conversions\n from `f64` [[#757]](https://github.com/extendr/extendr/pull/757)\n\n## 0.6.0\n\n### Added\n\n- `ALTLIST` support allowing users to represent structs as R list objects\n- [**either**] `TryFrom<&Robj> for Either` and `From> for Robj` if `T` and `R` are themselves implement these traits. This unblocks scenarios like accepting any numeric vector from R via `Either` without extra memory allocation [[#480]](https://github.com/extendr/extendr/pull/480)\n- `PartialOrd` trait implementation for `Rfloat`, `Rint` and `Rbool`. `Rfloat` and `Rint` gained `min()` and `max()` methods [[#573]](https://github.com/extendr/extendr/pull/573)\n- `use_rng` option for the `extendr` attribute macro, which enables the use of\nrandom number sampling methods from R, e.g. `#[extendr(use_rng = true)` [[#476]](https://github.com/extendr/extendr/pull/476)\n- `[T; N]` conversions to `Robj` [[#594]](https://github.com/extendr/extendr/pull/594/)\n- `ToVectorValue` for `Rfloat`, `Rint` and `Rbool` [[#593]](https://github.com/extendr/extendr/pull/593)\n- `TryFrom<_>` on `Vec<_>` for `Integers` (`i32`), `Complexes` (`c64`), `Doubles` (`f64`), and `Logicals` (`bool` / `i32`). [[#593]](https://github.com/extendr/extendr/pull/593)\n- `Rstr` can now be constructed from `Option` [[#630]](https://github.com/extendr/extendr/pull/630)\n\n### Fixed\n\n- You can now create `ArrayView1` from `&Robj` as well as `Robj`\n [[#501]](https://github.com/extendr/extendr/pull/501)\n- Raw literals from Rust can be used for function and argument names. e.g.\n `fn r#type()` in Rust is converted to `type()` in R.\n [[#531]](https://github.com/extendr/extendr/pull/531)\n- Fix memory leaks on errors and panics\n [[#555]](https://github.com/extendr/extendr/pull/555)\n- Fixed error when collecting too many objects into `List`, etc.\n [[#540]](https://github.com/extendr/extendr/pull/540)\n\n## 0.4.0\n\n### Added\n\n- Support for setting the default value of arguments to struct methods, using `#[default = \"...\"]` [[#436]](https://github.com/extendr/extendr/pull/436)\n- [**ndarray**] `TryFrom<&Robj>` for `ArrayView1` and `ArrayView2`, where `T` is `i32`, `f64`, `c64`, `Rint`, `Rfloat`, `Rcplx`, `Rstr`, `Rbool` [[#443]](https://github.com/extendr/extendr/pull/443)\n- `Debug` trait implementation for `Rcplx` and `Complexes` [[#444]](https://github.com/extendr/extendr/pull/444)\n- `TryFrom`, `From>`, `Into>` and their variations for `Nullable` [[#446]](https://github.com/extendr/extendr/pull/446)\n- `Nullable::map()` that acts on not null value and propagates `NULL` [[#446]](https://github.com/extendr/extendr/pull/446)\n- [**ndarray**] Conversion from owned arrays (ie `ndarray::Array`) into `Robj` [[#450]](https://github.com/extendr/extendr/pull/450)\n- [**ndarray**][**docs**] Documentation for the `robj_ndarray` module [[#450]](https://github.com/extendr/extendr/pull/450)\n- `Sum` for scalars like `Rint`, `Rfloat` and `Rcplx`, which accept `Iterator` [[#454]](https://github.com/extendr/extendr/pull/454)\n- A new `collect_rarray` method that can be used to collect arbitrary iterables into an R matrix\n [[#466]](https://github.com/extendr/extendr/pull/466)\n- [**docs**] Documentation for `RArray::new_matrix()` [[#466]](https://github.com/extendr/extendr/pull/466)\n\n### Changed\n\n- [**docs**] Use bindgen on docs.rs, to ensure newer R features will still be documented [[#426]](https://github.com/extendr/extendr/pull/426)\n- Unify the tagging mechanism used to identify Rust types inside `ExternalPtr`. This allows `#[extendr]`-annotated functions to directly accept `ExternalPtr` as well as `MyStruct` [[#433]](https://github.com/extendr/extendr/pull/433)\n- `Nullable` is now part of `extendr_api::prelude` [[#446]](https://github.com/extendr/extendr/pull/446)\n- Bump the Rust edition from 2018 to 2021 [[#458]](https://github.com/extendr/extendr/pull/458)\n- When converted to `STRSXP`, strings are now correctly marked as UTF-8 even on non-UTF-8 platforms (i.e., R < 4.2 on Windows), which shouldn't matter for most of the users [[#467]](https://github.com/extendr/extendr/pull/467)\n\n### Fixed\n\n- The R CMD check note \"Found non-API calls to R\" by moving `use extendr_engine;` inside `test!` macro [[#424]](https://github.com/extendr/extendr/pull/424)\n- The clippy lint \"this public function might dereference a raw pointer but is not marked `unsafe`\" [[#451]](https://github.com/extendr/extendr/pull/451)\n- A bug where importing a submodule via `use some_module;` inside the `extendr_module!` macro wasn't working [[#469]](https://github.com/extendr/extendr/pull/469)\n\n## 0.3.0\n\n### Added\n\n- `Function` type that wraps an R function, which can be invoked using the `call()` method. [[#188]](https://github.com/extendr/extendr/pull/188)\n- `pairlist!` macro for generating `Pairlist` objects, e.g. for use in function calls. [[#202]](https://github.com/extendr/extendr/pull/202)\n- `use_try_from` option for the `extendr` macro, which allows the use of any type that implements `TryInto`/`TryFrom`, e.g. `#[extendr(use_try_from = true)]`. [[#222]](https://github.com/extendr/extendr/pull/222)\n- Support for R version 4.2. [[#235]](https://github.com/extendr/extendr/issues/235)\n- `call!` macro, which can be used to call an R function whose name is provided as a string. [[#238]](https://github.com/extendr/extendr/pull/238)\n- Large Rust integer types (`u32`, `u64` and `i64`) can now be converted to R's `numeric` type, which can handle large integer values. [[#242]](https://github.com/extendr/extendr/pull/242)\n- `TryFrom` for a large number of Rust types. [[#249]](https://github.com/extendr/extendr/pull/249), [[#258]](https://github.com/extendr/extendr/pull/258)\n- Support for `ALTREP`. [[#250]](https://github.com/extendr/extendr/pull/250), [[#274]](https://github.com/extendr/extendr/pull/274)\n- `S4` struct, which wraps an S4 class in R. [[#268]](https://github.com/extendr/extendr/pull/268)\n- [**ndarray**] Implemented `TryFrom<&ArrayBase> for Robj`, allowing `extendr`-annotated functions to return Arrays from the `ndarray` crate and have them automatically converted to R arrays. [[#275]](https://github.com/extendr/extendr/pull/275)\n- `Rint`, `Rdouble`, `Rbool` and `Rcplx`: `NA`-aware wrappers for scalar elements of R vectors [[#274]](https://github.com/extendr/extendr/pull/274), [[#284]](https://github.com/extendr/extendr/pull/284), [[#301]](https://github.com/extendr/extendr/pull/301), [[#338]](https://github.com/extendr/extendr/pull/338), [[#350]](https://github.com/extendr/extendr/pull/350)\n- `Integers`, `Doubles`, `Strings`, `Logicals` and `Complexes`: wrappers for R vectors that deref to slices of the above types (`RInt` etc). [[#274]](https://github.com/extendr/extendr/pull/274), [[#284]](https://github.com/extendr/extendr/pull/284), [[#301]](https://github.com/extendr/extendr/pull/301), [[#338]](https://github.com/extendr/extendr/pull/338), [[#350]](https://github.com/extendr/extendr/pull/350)\n- `ExternalPtr`, a wrapper class for creating R objects containing any Rust object. [[#260]](https://github.com/extendr/extendr/pull/260)\n- [**graphics**] Support for R graphics and graphics devices. The `graphics` feature flag is disabled by default. [[#279]](https://github.com/extendr/extendr/pull/279), [[#360]](https://github.com/extendr/extendr/pull/360), [[#373]](https://github.com/extendr/extendr/pull/373), [[#379]](https://github.com/extendr/extendr/pull/379), [[#380]](https://github.com/extendr/extendr/pull/380), [[#389]](https://github.com/extendr/extendr/pull/389)\n- `Deref` implementation for vector types (Rint/Rfloat/Rbool/Rstr/Robj) to appropriately typed Rust slices. [[#327]](https://github.com/extendr/extendr/pull/327)\n- `default` option for `extendr`-annotated functions, allowing them to have default values, e.g. `fn fred(#[default=\"NULL\"] x: Option) { }`. [[#334]](https://github.com/extendr/extendr/pull/334)\n- `r_name` option for `extendr`-annotated functions, allowing the generated R function to have a different name. e.g.\n\n ```rust\n #[extendr(\n use_try_from = true,\n r_name = \"test.rename.rlike\",\n mod_name = \"test_rename_mymod\"\n )]\n fn test_rename() { }\n ```\n\n [[#335]](https://github.com/extendr/extendr/pull/335)\n- `serde::Serialize` implementation for R types. [[#305]](https://github.com/extendr/extendr/pull/305), [[#355]](https://github.com/extendr/extendr/pull/355)\n- `Rany` type and the `as_any` conversion method. [[#320]](https://github.com/extendr/extendr/pull/320)\n- `std::fmt::Debug` implementation for wrapper types. [[#345]](https://github.com/extendr/extendr/pull/345)\n- `#[derive(TryFromRobj)` and `#[derive(IntoRobj)]` which provide an automatic conversion from and to any custom Rust struct and `Robj` [[#347]](https://github.com/extendr/extendr/pull/347)\n- `[[` operator that works with Rust classes. Its behavior is identical to that of the `$` operator. [[#359]](https://github.com/extendr/extendr/pull/359)\n- `Load` and `Save`, traits that, once implemented, provide the ability to load and save R data in the RDS format. These traits are implemented for all `Robj`. [[#363]](https://github.com/extendr/extendr/pull/363)\n- `Dataframe` wrapper struct. [[#393]](https://github.com/extendr/extendr/pull/393)\n- `IntoDataFrame` trait, which can be derived to allow arbitrary Rust structs to be converted to rows of a data frame. [[#393]](https://github.com/extendr/extendr/pull/393)\n\n### Changed\n\n- `Strings::elt` now returns an `Rstr`. [[#345]](https://github.com/extendr/extendr/pull/345)\n- Renamed `RType` to `Rtype`. [[#345]](https://github.com/extendr/extendr/pull/345)\n- Wrapper types now contain `Robj` fields. [[#190]](https://github.com/extendr/extendr/pull/190)\n- The `R!` macro now accepts strings that contain R code. This is now the recommended way of using the macro, especially with raw strings e.g.\n\n ```rust\n R!(r#\"\n print(\"hello\")\n \"#);\n ```\n\n [[#203]](https://github.com/extendr/extendr/pull/203)\n- Improved error handling for `<&str>::try_from(Robj)`. [[#226]](https://github.com/extendr/extendr/pull/226)\n- `SymPair::sym_pair()` now returns `(Option, Robj)`. [[#225]](https://github.com/extendr/extendr/pull/225)\n- More detailed error messages when converting Rust integer types to R. [[#243]](https://github.com/extendr/extendr/pull/243)\n- `Character` is now called `Rstr`. [[#273]](https://github.com/extendr/extendr/pull/273)\n- [**ndarray**] Bumped `ndarray` to 0.15.3. Under [RFC 1977](https://github.com/rust-lang/rfcs/pull/1977) this is a \"public dependency\" change, and therefore can be considered a breaking change, as consumers of `extendr` that use an older version of `ndarray` will no longer be compatible until they also bump `ndarray` to a compatible version. [[#275]](https://github.com/extendr/extendr/pull/275)\n- `IsNA` trait has been renamed to `CanBeNA`. [[#288]](https://github.com/extendr/extendr/pull/288)\n- `list!` has been rewritten, and now returns a `List` struct. [[#303]](https://github.com/extendr/extendr/pull/303)\n\n### Deprecated\n\n- Calling the `R!` macro with non-string types (e.g. `R!(1)`) is now deprecated. [[#203]](https://github.com/extendr/extendr/pull/203)\n\n### Removed\n\n- `Real`, `Int`, `Bool` and the redundant trait `SliceIter`, which should be replaced with `Rdouble`, `Rint`, and `Rbool` respectively. [[#304]](https://github.com/extendr/extendr/pull/304), [[#338]](https://github.com/extendr/extendr/pull/338)\n- `TryFrom` conversions between `Robj` and `HashMap` for consistency. `List::into_hashmap()` and `List::from_hashmap()` should be used instead. [[#254]](https://github.com/extendr/extendr/pull/254)\n\n## extendr 0.2.0\n\n- Added contributing guidelines and code of conduct.\n\n- Made use of ndarray optional.\n\n- Made #[extendr] calls panic and thread safe.\n\n- Added NA handling to the #[extendr] macro.\n\n- Added a separate extendr-engine crate that is needed when calling R from Rust.\n\n- Wrapper classes for pairlists, environment, raw, symbols and others.\n\n- More iterator support.\n\n- Operators index, slice, dollar, double_colon, +, -, * and /`.\n\n- Debug printing support expanded to use wrappers.\n\n- Conversion of Robj to wrapper types.\n\n- Multithreaded support - allows multithreaded testing using a recursive spinlock.\n\n- Bool type extended and implemented using TRUE, FALSE and NA_BOOLEAN.\n\n- Optional parameters to support NA handing.\n\n- Errors thrown if input parameters without Option are NA.\n\n- Harmonising of function names into integer, real, logical, symbol, raw, list, pairlist and env.\n\n- Refactored robj code into several source files.\n\n- Many functions updated to use generic types.\n\n- R! macro for executing R source code.\n\n- call! macro to call R code.\n\n- sym! macro to generate symbols.\n\n- Simplification of vector generation using collect_robj and ToVectorValue.\n\n- Added array types `[1, 2, 3]` as `Robj::from` targets.\n\n- Macros now mostly return errors.\n\n## extendr 0.1.10\n\n- Fix build on Windows and MacOS.\n", "supporting": [], "filters": [ "rmarkdown/pagebreak.lua" diff --git a/_freeze/user-guide/complete-example/execute-results/html.json b/_freeze/user-guide/complete-example/execute-results/html.json index aec2d49..612def5 100644 --- a/_freeze/user-guide/complete-example/execute-results/html.json +++ b/_freeze/user-guide/complete-example/execute-results/html.json @@ -2,7 +2,7 @@ "hash": "017d253141d50e842b134e50a8ceacff", "result": { "engine": "knitr", - "markdown": "---\ntitle: \"A Complete Example\"\nsubtitle: \"A package from start to finish: Making a heckin' case converter.\"\n---\n\n\nThe Rust crate ecosystem is rich with very small and very powerful utility libraries. One of the most downloaded crates is [heck](https://docs.rs/heck). It provides traits and structs to perform some of the most common case conversions.\n\nIn this tutorial we'll create a 0 dependency R package to provide the common case conversions. The resultant R package will be more performant but less flexible than the [`{snakecase}`](https://tazinho.github.io/snakecase/) R package. \n\nThis tutorial covers: \n\n- vectorization\n- `NA` handling\n- code generation using a macro\n\n## Getting started\n\nCreate a new R package:\n\n```r\nusethis::create_package(\"heck\")\n```\n\nWhen the new R package has opened up, add `extendr`.\n\n```r\nrextendr::use_extendr(crate_name = \"rheck\", lib_name = \"rheck\")\n```\n\n::: callout-note\nWhen adding the extendr dependency, make sure that the `crate_name` and `lib_name` arguments _are not_ `heck`. In order to add the `heck` crate as a dependency, the crate itself cannot be called `heck` because it creates a recursive dependency. Doing this allows us to name the R package `{heck}`, but the internal Rust crate is called `rheck`.\n:::\n\nNext, `heck` is needed as a dependency. From your terminal, navigate to `src/rust` and run `cargo add heck`. With this, you have everything you need to get started.\n\n\n## snek case conversion\n\n\n\n\n::: {.cell}\n\n```{.rust .cell-code}\nuse heck::ToSnekCase;\n```\n:::\n\n\nLet's start by creating a simple function to take a single string, and convert it to snake case. First, the trait `ToSnekCase` needs to be imported so that the method `to_snek_case()` is available to `&str`.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\nuse heck::ToSnekCase;\n\n#[extendr]\nfn to_snek_case(x: &str) -> String {\n x.to_snek_case()\n}\n```\n:::\n\n\nSimple enough, right? Let's give it a shot. To make it accessible from your R session, it needs to be included in your `extendr_module! {}` macro. \n\n```rust\nextendr_module! {\n mod heck;\n fn to_snek_case;\n}\n```\n\nFrom your R session, run `rextendr::document()` followed by `devtools::load_all()` to make the function available. We'll skip these step from now on, but be sure to remember it!\n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(\"MakeMe-Snake case\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"make_me_snake_case\"\n```\n\n\n:::\n:::\n\n\nRarely is it useful to run a function on just a scalar character value. Rust, though, works with scalars by default and adding vectorization is another step. \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(c(\"DontStep\", \"on-Snek\"))\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in to_snek_case(c(\"DontStep\", \"on-Snek\")): Expected Scalar, got Strings\n```\n\n\n:::\n:::\n\n\nProviding a character vector causes an error. So how do you go about vectorizing? \n\n## vectorizing snek case conversion\n\nTo vectorize this function, you need to be apply the conversion to each element in a character vector. The extendr wrapper struct for a character vector is called `Strings`. To take in a character vector and also return one, the function signature should look like this:\n\n```rust\n#[extendr]\nfn to_snek_case(x: Strings) -> Strings {\n}\n```\n\nThis says there is an argument `x` which must be a character vector and this function must also `->` return the `Strings` (a character vector).\n\nTo iterate through this you can use the `.into_iter()` method on the character vector. \n\n```rust\n#[extendr]\nfn to_snek_case(x: Strings) -> Strings {\n x\n .into_iter()\n // the rest of the function\n}\n```\n\nIterators have a method called `.map()` (yes, just like `purrr::map()`). It lets you apply a closure (an anonymous function) to each element of the iterator. In this case, each element is an [`Rstr`](https://extendr.github.io/extendr/extendr_api/wrapper/rstr/struct.Rstr.html). The `Rstr` has a method `.as_str()` which will return a string slice `&str`. You can take this slice and pass it on to `.to_snek_case()`. After having mapped over each element, the results are `.collect()`ed into another `Strings`. \n\n\n\n::: {.cell preamble='use_heck'}\n\n```{.rust .cell-code}\n#[extendr]\nfn to_snek_case(x: Strings) -> Strings {\n x\n .into_iter()\n .map(|xi| {\n xi.as_str().to_snek_case()\n })\n .collect::()\n}\n```\n:::\n\n\n\nThis new version of the function can be used in a vectorized manner: \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(c(\"DontStep\", \"on-Snek\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"dont_step\" \"on_snek\" \n```\n\n\n:::\n:::\n\n\nBut can it handle a missing value out of the box? \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(c(\"DontStep\", NA_character_, \"on-Snek\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"dont_step\" \"na\" \"on_snek\" \n```\n\n\n:::\n:::\n\n\nWell, sort of. The `as_str()` method when used on a missing value will return `\"NA\"` which is not in a user's best interest. \n\n\n## handling missing values\n\nInstead of returning `\"na\"`, it would be better to return an _actual_ missing value. Those can be created each scalar's `na()` method e.g. `Rstr::na()`. \n\nYou can modify the `.map()` statement to check if an `NA` is present, and, if so, return an `NA` value. To perform this check, use the `is_na()` method which returns a `bool` which is either `true` or `false`. The result can be [`match`ed](https://doc.rust-lang.org/book/ch06-02-match.html). When it is missing, the match arm returns the `NA` scalar value. When it is not missing, the `Rstr` is converted to snek case. However, since the `true` arm is an `Rstr` the other `false` arm must _also_ be an `Rstr`. To accomplish this use the `Rstr::from()` method. \n\n\n::: {.cell preamble='use_heck' profile='release'}\n\n```{.rust .cell-code}\n#[extendr]\nfn to_snek_case(x: Strings) -> Strings {\n x.into_iter()\n .map(|xi| match xi.is_na() {\n true => Rstr::na(),\n false => Rstr::from(xi.as_str().to_snek_case()),\n })\n .collect::()\n}\n```\n:::\n\n\nThis function can now handle missing values! \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(c(\"DontStep\", NA_character_, \"on-Snek\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"dont_step\" NA \"on_snek\" \n```\n\n\n:::\n:::\n\n\n## automating other methods with a macro! \n\nThere are traits for the other case conversions such as `ToKebabCase`, `ToPascalCase`, `ToShoutyKebabCase` and others. The each have a similar method name: `.to_kebab_case()`, `to_pascal_case()`, `.to_shouty_kebab_case()`. You can either choose to copy the above and change the method call multiple times, _or_ use a macro as a form of code generation. \n\nA macro allows you to generate code in a short hand manner. This macro take an identifier which has a placeholder called `$fn_name`: `$fn_name:ident`. \n\n```rust\nmacro_rules! make_heck_fn {\n ($fn_name:ident) => {\n #[extendr]\n /// @export\n fn $fn_name(x: Strings) -> Strings {\n x.into_iter()\n .map(|xi| match xi.is_na() {\n true => Rstr::na(),\n false => Rstr::from(xi.as_str().$fn_name()),\n })\n .collect::()\n }\n };\n}\n```\n\nThe `$fn_name` placeholder is put as the function name definition which is the same as the method name. To use this macro to generate the rest of the functions the other traits need to be imported.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\nuse heck::{\n ToKebabCase, ToShoutyKebabCase,\n ToSnekCase, ToShoutySnakeCase,\n ToPascalCase, ToUpperCamelCase,\n ToTrainCase, ToTitleCase,\n};\n```\n:::\n\n\nWith the traits in scope, the macro can be invoked to generate the other functions.\n\n```rust\nmake_heck_fn!(to_snek_case);\nmake_heck_fn!(to_shouty_snake_case);\nmake_heck_fn!(to_kebab_case);\nmake_heck_fn!(to_shouty_kebab_case);\nmake_heck_fn!(to_pascal_case);\nmake_heck_fn!(to_upper_camel_case);\nmake_heck_fn!(to_train_case);\nmake_heck_fn!(to_title_case);\n```\n\nNote that each of these functions should be added to the `extendr_module! {}` macro in order for them to be available from R. \n\n\n\n\n\nTest it out with the `to_shouty_kebab_case()` function! \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_shouty_kebab_case(\"lorem:IpsumDolor__sit^amet\")\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"LOREM-IPSUM-DOLOR-SIT-AMET\"\n```\n\n\n:::\n:::\n\n\nAnd with that, you've created an R package that provides case conversion using heck and with very little code!\n\n\n## bench marking with `{snakecase}`\n\nTo illustrate the performance gains from using a vectorized Rust funciton, a `bench::mark()` is created between `to_snek_case()` and `snakecase::to_snake_case()`.\n\n\n\n\n\nThe bench mark will use 5000 randomly generated lorem ipsum sentences. \n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- unlist(lorem::ipsum(5000, 1, 25))\n\nhead(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"Adipiscing porttitor ac gravida scelerisque quisque sapien cum est duis metus penatibus cras himenaeos interdum senectus sollicitudin nisi malesuada faucibus semper sodales potenti rutrum eu?\"\n[2] \"Ipsum laoreet laoreet laoreet rutrum senectus id lectus erat velit mattis felis tellus mus neque augue libero dapibus auctor cursus lacinia egestas nullam ultrices ultrices porttitor cursus?\" \n[3] \"Amet sollicitudin a erat sagittis id massa lobortis justo metus vehicula purus ad orci non et magnis dignissim metus dictum diam euismod a?\" \n[4] \"Sit torquent vivamus blandit molestie semper primis pretium tempus faucibus nisi magnis sed cursus vehicula venenatis praesent pretium ad montes lectus a vel magna arcu praesent dui aenean?\" \n[5] \"Lorem platea magnis interdum metus cubilia venenatis elementum habitant morbi iaculis aenean sagittis metus luctus conubia quisque tempus pellentesque inceptos sed nisi?\" \n[6] \"Lorem tristique senectus orci diam parturient non ut nunc condimentum enim parturient mus neque vel porttitor lacus litora fusce sociosqu mauris ligula auctor?\" \n```\n\n\n:::\n\n```{.r .cell-code}\nbench::mark(\n rust = to_snek_case(x),\n snakecase = snakecase::to_snake_case(x)\n)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n# A tibble: 2 × 6\n expression min median `itr/sec` mem_alloc `gc/sec`\n \n1 rust 23.7ms 24.3ms 40.4 1.17MB 0 \n2 snakecase 305.8ms 307ms 3.26 12.29MB 4.89\n```\n\n\n:::\n:::\n\n\n\n\n## The whole thing\n\nIn just 42 lines of code (empty lines included), you can create a very performant R package! \n\n```rust\nuse extendr_api::prelude::*;\n\nuse heck::{\n ToKebabCase, ToPascalCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnekCase, ToTitleCase,\n ToTrainCase, ToUpperCamelCase,\n};\n\nmacro_rules! make_heck_fn {\n ($fn_name:ident) => {\n #[extendr]\n /// @export\n fn $fn_name(x: Strings) -> Strings {\n x.into_iter()\n .map(|xi| match xi.is_na() {\n true => Rstr::na(),\n false => Rstr::from(xi.as_str().$fn_name()),\n })\n .collect::()\n }\n };\n}\n\nmake_heck_fn!(to_snek_case);\nmake_heck_fn!(to_shouty_snake_case);\nmake_heck_fn!(to_kebab_case);\nmake_heck_fn!(to_shouty_kebab_case);\nmake_heck_fn!(to_pascal_case);\nmake_heck_fn!(to_upper_camel_case);\nmake_heck_fn!(to_train_case);\nmake_heck_fn!(to_title_case);\n\nextendr_module! {\n mod heck;\n fn to_snek_case;\n fn to_shouty_snake_case;\n fn to_kebab_case;\n fn to_shouty_kebab_case;\n fn to_pascal_case;\n fn to_upper_camel_case;\n fn to_title_case;\n fn to_train_case;\n}\n```", + "markdown": "---\ntitle: \"A Complete Example\"\nsubtitle: \"A package from start to finish: Making a heckin' case converter.\"\n---\n\n\nThe Rust crate ecosystem is rich with very small and very powerful utility libraries. One of the most downloaded crates is [heck](https://docs.rs/heck). It provides traits and structs to perform some of the most common case conversions.\n\nIn this tutorial we'll create a 0 dependency R package to provide the common case conversions. The resultant R package will be more performant but less flexible than the [`{snakecase}`](https://tazinho.github.io/snakecase/) R package. \n\nThis tutorial covers: \n\n- vectorization\n- `NA` handling\n- code generation using a macro\n\n## Getting started\n\nCreate a new R package:\n\n```r\nusethis::create_package(\"heck\")\n```\n\nWhen the new R package has opened up, add `extendr`.\n\n```r\nrextendr::use_extendr(crate_name = \"rheck\", lib_name = \"rheck\")\n```\n\n::: callout-note\nWhen adding the extendr dependency, make sure that the `crate_name` and `lib_name` arguments _are not_ `heck`. In order to add the `heck` crate as a dependency, the crate itself cannot be called `heck` because it creates a recursive dependency. Doing this allows us to name the R package `{heck}`, but the internal Rust crate is called `rheck`.\n:::\n\nNext, `heck` is needed as a dependency. From your terminal, navigate to `src/rust` and run `cargo add heck`. With this, you have everything you need to get started.\n\n\n## snek case conversion\n\n\n\n\n::: {.cell}\n\n```{.rust .cell-code}\nuse heck::ToSnekCase;\n```\n:::\n\n\nLet's start by creating a simple function to take a single string, and convert it to snake case. First, the trait `ToSnekCase` needs to be imported so that the method `to_snek_case()` is available to `&str`.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\nuse heck::ToSnekCase;\n\n#[extendr]\nfn to_snek_case(x: &str) -> String {\n x.to_snek_case()\n}\n```\n:::\n\n\nSimple enough, right? Let's give it a shot. To make it accessible from your R session, it needs to be included in your `extendr_module! {}` macro. \n\n```rust\nextendr_module! {\n mod heck;\n fn to_snek_case;\n}\n```\n\nFrom your R session, run `rextendr::document()` followed by `devtools::load_all()` to make the function available. We'll skip these step from now on, but be sure to remember it!\n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(\"MakeMe-Snake case\")\n#> [1] \"make_me_snake_case\"\n```\n:::\n\n\nRarely is it useful to run a function on just a scalar character value. Rust, though, works with scalars by default and adding vectorization is another step. \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(c(\"DontStep\", \"on-Snek\"))\n#> Error in to_snek_case(c(\"DontStep\", \"on-Snek\")): Expected Scalar, got Strings\n```\n:::\n\n\nProviding a character vector causes an error. So how do you go about vectorizing? \n\n## vectorizing snek case conversion\n\nTo vectorize this function, you need to be apply the conversion to each element in a character vector. The extendr wrapper struct for a character vector is called `Strings`. To take in a character vector and also return one, the function signature should look like this:\n\n```rust\n#[extendr]\nfn to_snek_case(x: Strings) -> Strings {\n}\n```\n\nThis says there is an argument `x` which must be a character vector and this function must also `->` return the `Strings` (a character vector).\n\nTo iterate through this you can use the `.into_iter()` method on the character vector. \n\n```rust\n#[extendr]\nfn to_snek_case(x: Strings) -> Strings {\n x\n .into_iter()\n // the rest of the function\n}\n```\n\nIterators have a method called `.map()` (yes, just like `purrr::map()`). It lets you apply a closure (an anonymous function) to each element of the iterator. In this case, each element is an [`Rstr`](https://extendr.github.io/extendr/extendr_api/wrapper/rstr/struct.Rstr.html). The `Rstr` has a method `.as_str()` which will return a string slice `&str`. You can take this slice and pass it on to `.to_snek_case()`. After having mapped over each element, the results are `.collect()`ed into another `Strings`. \n\n\n\n::: {.cell preamble='use_heck'}\n\n```{.rust .cell-code}\n#[extendr]\nfn to_snek_case(x: Strings) -> Strings {\n x\n .into_iter()\n .map(|xi| {\n xi.as_str().to_snek_case()\n })\n .collect::()\n}\n```\n:::\n\n\n\nThis new version of the function can be used in a vectorized manner: \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(c(\"DontStep\", \"on-Snek\"))\n#> [1] \"dont_step\" \"on_snek\"\n```\n:::\n\n\nBut can it handle a missing value out of the box? \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(c(\"DontStep\", NA_character_, \"on-Snek\"))\n#> [1] \"dont_step\" \"na\" \"on_snek\"\n```\n:::\n\n\nWell, sort of. The `as_str()` method when used on a missing value will return `\"NA\"` which is not in a user's best interest. \n\n\n## handling missing values\n\nInstead of returning `\"na\"`, it would be better to return an _actual_ missing value. Those can be created each scalar's `na()` method e.g. `Rstr::na()`. \n\nYou can modify the `.map()` statement to check if an `NA` is present, and, if so, return an `NA` value. To perform this check, use the `is_na()` method which returns a `bool` which is either `true` or `false`. The result can be [`match`ed](https://doc.rust-lang.org/book/ch06-02-match.html). When it is missing, the match arm returns the `NA` scalar value. When it is not missing, the `Rstr` is converted to snek case. However, since the `true` arm is an `Rstr` the other `false` arm must _also_ be an `Rstr`. To accomplish this use the `Rstr::from()` method. \n\n\n::: {.cell preamble='use_heck' profile='release'}\n\n```{.rust .cell-code}\n#[extendr]\nfn to_snek_case(x: Strings) -> Strings {\n x.into_iter()\n .map(|xi| match xi.is_na() {\n true => Rstr::na(),\n false => Rstr::from(xi.as_str().to_snek_case()),\n })\n .collect::()\n}\n```\n:::\n\n\nThis function can now handle missing values! \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_snek_case(c(\"DontStep\", NA_character_, \"on-Snek\"))\n#> [1] \"dont_step\" NA \"on_snek\"\n```\n:::\n\n\n## automating other methods with a macro! \n\nThere are traits for the other case conversions such as `ToKebabCase`, `ToPascalCase`, `ToShoutyKebabCase` and others. The each have a similar method name: `.to_kebab_case()`, `to_pascal_case()`, `.to_shouty_kebab_case()`. You can either choose to copy the above and change the method call multiple times, _or_ use a macro as a form of code generation. \n\nA macro allows you to generate code in a short hand manner. This macro take an identifier which has a placeholder called `$fn_name`: `$fn_name:ident`. \n\n```rust\nmacro_rules! make_heck_fn {\n ($fn_name:ident) => {\n #[extendr]\n /// @export\n fn $fn_name(x: Strings) -> Strings {\n x.into_iter()\n .map(|xi| match xi.is_na() {\n true => Rstr::na(),\n false => Rstr::from(xi.as_str().$fn_name()),\n })\n .collect::()\n }\n };\n}\n```\n\nThe `$fn_name` placeholder is put as the function name definition which is the same as the method name. To use this macro to generate the rest of the functions the other traits need to be imported.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\nuse heck::{\n ToKebabCase, ToShoutyKebabCase,\n ToSnekCase, ToShoutySnakeCase,\n ToPascalCase, ToUpperCamelCase,\n ToTrainCase, ToTitleCase,\n};\n```\n:::\n\n\nWith the traits in scope, the macro can be invoked to generate the other functions.\n\n```rust\nmake_heck_fn!(to_snek_case);\nmake_heck_fn!(to_shouty_snake_case);\nmake_heck_fn!(to_kebab_case);\nmake_heck_fn!(to_shouty_kebab_case);\nmake_heck_fn!(to_pascal_case);\nmake_heck_fn!(to_upper_camel_case);\nmake_heck_fn!(to_train_case);\nmake_heck_fn!(to_title_case);\n```\n\nNote that each of these functions should be added to the `extendr_module! {}` macro in order for them to be available from R. \n\n\n\n\n\nTest it out with the `to_shouty_kebab_case()` function! \n\n\n::: {.cell}\n\n```{.r .cell-code}\nto_shouty_kebab_case(\"lorem:IpsumDolor__sit^amet\")\n#> [1] \"LOREM-IPSUM-DOLOR-SIT-AMET\"\n```\n:::\n\n\nAnd with that, you've created an R package that provides case conversion using heck and with very little code!\n\n\n## bench marking with `{snakecase}`\n\nTo illustrate the performance gains from using a vectorized Rust funciton, a `bench::mark()` is created between `to_snek_case()` and `snakecase::to_snake_case()`.\n\n\n\n\n\nThe bench mark will use 5000 randomly generated lorem ipsum sentences. \n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- unlist(lorem::ipsum(5000, 1, 25))\n\nhead(x)\n#> [1] \"Consectetur velit mi integer facilisi quam nulla dui pharetra mattis mus gravida porttitor eleifend et donec varius nisl dictumst nisi metus aptent fringilla natoque porta nibh sapien enim.\" \n#> [2] \"Adipiscing hendrerit ad per duis aliquam vel cursus suscipit semper mi malesuada feugiat pharetra rutrum libero placerat consequat congue eleifend duis vivamus natoque dignissim molestie volutpat arcu a ad purus vitae euismod cursus bibendum ultricies pulvinar dui eu?\"\n#> [3] \"Ipsum dapibus metus donec eros potenti natoque massa enim enim duis dictum magnis viverra metus fames integer varius eu neque varius nascetur mollis natoque.\" \n#> [4] \"Amet magnis faucibus est non vivamus sollicitudin viverra nascetur nibh feugiat in nisl tempor ad metus conubia duis per justo est eleifend morbi per imperdiet accumsan taciti tempor fames mattis.\" \n#> [5] \"Ipsum dignissim primis duis ullamcorper nam tempor placerat tincidunt sociis commodo et interdum ornare vel imperdiet ultricies eu porta ligula duis turpis litora.\" \n#> [6] \"Ipsum justo eros nullam per purus interdum lacus natoque tristique purus volutpat habitant nec duis litora velit et eros tortor placerat ultrices nullam ut class lacus ad iaculis.\"\n\nbench::mark(\n rust = to_snek_case(x),\n snakecase = snakecase::to_snake_case(x)\n)\n#> # A tibble: 2 × 6\n#> expression min median `itr/sec` mem_alloc `gc/sec`\n#> \n#> 1 rust 13.8ms 14ms 69.7 1.17MB 0 \n#> 2 snakecase 201.2ms 211ms 4.81 12.29MB 8.02\n```\n:::\n\n\n\n\n## The whole thing\n\nIn just 42 lines of code (empty lines included), you can create a very performant R package! \n\n```rust\nuse extendr_api::prelude::*;\n\nuse heck::{\n ToKebabCase, ToPascalCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnekCase, ToTitleCase,\n ToTrainCase, ToUpperCamelCase,\n};\n\nmacro_rules! make_heck_fn {\n ($fn_name:ident) => {\n #[extendr]\n /// @export\n fn $fn_name(x: Strings) -> Strings {\n x.into_iter()\n .map(|xi| match xi.is_na() {\n true => Rstr::na(),\n false => Rstr::from(xi.as_str().$fn_name()),\n })\n .collect::()\n }\n };\n}\n\nmake_heck_fn!(to_snek_case);\nmake_heck_fn!(to_shouty_snake_case);\nmake_heck_fn!(to_kebab_case);\nmake_heck_fn!(to_shouty_kebab_case);\nmake_heck_fn!(to_pascal_case);\nmake_heck_fn!(to_upper_camel_case);\nmake_heck_fn!(to_train_case);\nmake_heck_fn!(to_title_case);\n\nextendr_module! {\n mod heck;\n fn to_snek_case;\n fn to_shouty_snake_case;\n fn to_kebab_case;\n fn to_shouty_kebab_case;\n fn to_pascal_case;\n fn to_upper_camel_case;\n fn to_title_case;\n fn to_train_case;\n}\n```", "supporting": [], "filters": [ "rmarkdown/pagebreak.lua" diff --git a/_freeze/user-guide/type-mapping/extendr-macro/execute-results/html.json b/_freeze/user-guide/type-mapping/extendr-macro/execute-results/html.json index 305455b..9bd983d 100644 --- a/_freeze/user-guide/type-mapping/extendr-macro/execute-results/html.json +++ b/_freeze/user-guide/type-mapping/extendr-macro/execute-results/html.json @@ -1,8 +1,8 @@ { - "hash": "deb744a905ba60049b6ccc0bbceb3683", + "hash": "dc0abd82fc3b5e883c992ef3d8312104", "result": { "engine": "knitr", - "markdown": "---\ntitle: \"The extendr Macro\"\n---\n\n\nThe power of extendr is in its ability to use Rust from R. The `#[extendr]` macro is what determines what is exported to R from Rust. This section covers the basic usage of the `#[extendr]` macro. \n\n[`#[extendr]`](https://extendr.github.io/extendr/extendr_api/attr.extendr.html) is what is referred to as an [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros) (which itself is a type of [procedural macro](https://doc.rust-lang.org/reference/procedural-macros.html)). An attribute macro is attached to an [item](https://doc.rust-lang.org/reference/items.html) such as a function, `struct`, `enum`, or `impl`. \n\nThe `#[extendr]` attribute macro indicates that an item should be made available to R. However, it _can only be used_ with a function or an impl block. \n\n\n\n\n\n## Exporting functions \n\nIn order to make a function available to R, two things must happen. First, the `#[extendr]` macro must be attached to the function. For example, you can create a function `answer_to_life()`\n\n::: {.callout-note collapse=\"true\"}\nIn the Hitchhiker's Guide to the Galaxy, the number 42 is the answer to the universe. See this fun [article from Scientific American](https://www.scientificamerican.com/article/for-math-fans-a-hitchhikers-guide-to-the-number-42/)\n:::\n\n```rust\n#[extendr]\nfn answer_to_life() -> i32 {\n 42\n}\n```\n\nBy adding the `#[extendr]` attribute macro to the `answer_to_life()` function, we are indicating that this function has to be compatible with R. This alone, however, does not make the function available to R. It must be made available via the `extendr_module! {}` macro in `lib.rs`.\n\n```rust\nextendr_module! {\n mod hellorust;\n fn answer_to_life;\n}\n```\n\n::: callout-tip\nEverything that is made available in the `extendr_module! {}` macro in `lib.rs` must be compatible with R as indicated by the `#[extendr]` macro. Note that the module name `mod hellorust` must be the name of the R package that this is part of. If you have created your package with `rextendr::use_extendr()` this should be set automatically. See [Hello, world!](./hello-world.qmd).\n:::\n\nWhat happens if you try and return something that cannot be represented by R? Take this example, an enum `Shape` is defined and a function takes a string `&str`. Based on the value of the arugment, an enum variant is returned. \n\n```rust\n#[derive(Debug)]\nenum Shape {\n Triangle,\n Rectangle,\n Pentagon,\n Hexagon,\n}\n\n#[extendr]\nfn make_shape(shape: &str) -> Shape {\n match shape {\n \"triangle\" => Shape::Triangle,\n \"rectangle\" => Shape::Rectangle,\n \"pentagon\" => Shape::Pentagon,\n \"hexagon\" => Shape::Hexagon,\n &_ => unimplemented!()\n }\n}\n```\n\nWhen this is compiled, an error occurs because extendr does not know how to convert the `Shape` enum into something that R can use. The error is fairly informative! \n\n```\n#[extendr]\n | ^^^^^^^^^^ the trait `ToVectorValue` is not implemented for `Shape`, which is required by `extendr_api::Robj: From`\n |\n = help: the following other types implement trait `ToVectorValue`:\n bool\n i8\n i16\n i32\n i64\n usize\n u8\n u16\n and 45 others\n = note: required for `extendr_api::Robj` to implement `From`\n = note: this error originates in the attribute macro `extendr` \n```\n\nIt tells you that `Shape` does not implement the `ToVectorValue` trait. The `ToVectorValue` trait is what enables items from Rust to be returned to R.\n\n## `ToVectorValue` trait\n\nIn order for an item to be returned from a function marked with the `#[extendr]` attribute macro, it must be able to be turned into an R object. In extendr, the struct `Robj` is a catch all for any type of R object. \n\n::: callout-note\nFor those familiar with PyO3, the `Robj` struct is similar in concept to the [`PyAny`](https://docs.rs/pyo3/latest/pyo3/types/struct.PyAny.html) struct.\n::: \n\nThe `ToVectorValue` trait is what is used to convert Rust items into R objects. The trait is implemented on a number of standard Rust types such as `i32`, `f64`, `usize`, `String` and more (see [all foreign implementations here](https://extendr.github.io/extendr/extendr_api/robj/into_robj/trait.ToVectorValue.html#foreign-impls)) which enables these functions to be returned from a Rust function marked with `#[extendr]`. \n\n::: callout-note\nIn essence, all items that are returned from a function must be able to be turned into an `Robj`. Other extendr types such as `List`, for example, have a `From for Robj` implementation that defines how it is converted into an `Robj`.\n:::\n\nThis means that with a little extra work, the `Shape` enum can be returned to R. To do so, the `#[extendr]` macro needs to be added to an impl block. \n\n\n## Exporting `impl` blocks\n\nThe other supported item that can be made available to R is an [`impl`](https://doc.rust-lang.org/std/keyword.impl.html) block. \n`impl` is a keyword that allows you to _implement_ a trait or an inherent implementation. The `#[extendr]` macro works with inherent implementations. These are `impl`s on a type such as an `enum` or a `struct`. extendr _does not_ support using `#[extendr]` on trait impls. \n\n::: callout-note\nYou can only add an inherent implementation on a type that you have own and not provided by a third party crate. This would violate the [orphan rules](https://github.com/Ixrec/rust-orphan-rules?tab=readme-ov-file#what-are-the-orphan-rules).\n:::\n\nContinuing with the `Shape` example, this enum alone cannot be returned to R. For example, the following code will result in a compilation error\n\n```rust\n#[derive(Debug)]\nenum Shape {\n Triangle,\n Rectangle,\n Pentagon,\n Hexagon,\n}\n\n#[extendr]\nfn make_shape(shape: &str) -> Shape {\n match shape {\n \"triangle\" => Shape::Triangle,\n \"rectangle\" => Shape::Rectangle,\n \"pentagon\" => Shape::Pentagon,\n \"hexagon\" => Shape::Hexagon,\n &_ => unimplemented!()\n }\n}\n```\n```\nerror[E0277]: the trait bound `Shape: ToVectorValue` is not satisfied\n --> src/lib.rs:19:1\n |\n19 | #[extendr]\n | ^^^^^^^^^^ the trait `ToVectorValue` is not implemented for `Shape`, which is required by `extendr_api::Robj: From`\n |\n```\n\nHowever, if an impl block is added to the `Shape` enum, it can be returned to R. \n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[derive(Debug)]\nenum Shape {\n Triangle,\n Rectangle,\n Pentagon,\n Hexagon,\n}\n\n#[extendr]\nimpl Shape {\n fn new(x: &str) -> Self {\n match x {\n \"triangle\" => Self::Triangle,\n \"rectangle\" => Self::Rectangle,\n \"pentagon\" => Self::Pentagon,\n \"hexagon\" => Self::Hexagon,\n &_ => unimplemented!(),\n }\n }\n\n fn n_coords(&self) -> usize {\n match &self {\n Shape::Triangle => 3,\n Shape::Rectangle => 4,\n Shape::Pentagon => 4,\n Shape::Hexagon => 5,\n }\n }\n}\n```\n:::\n\n\nIn this example two new methods are added to the `Shape` enum. The first `new()` is like the `make_shape()` function that was shown earlier: it takes a `&str` and returns an enum variant. Now that the enum has an `impl` block with `#[extendr]` attribute macro, it can be exported to R by inclusion in the `extendr_module! {}` macro.\n\n```rust\nextendr_module! {\n mod hellorust;\n impl Shape;\n}\n```\n\nDoing so creates an environment in your package called `Shape`. The environment contains all of the methods that are available to you. \n\n::: callout-tip\nThere are use cases where you may not want to expose any methods but do want to make it possible to return a struct or an enum to the R. You can do this by adding an empty impl block with the `#[extendr]` attribute macro. \n::: \n\nIf you run `as.list(Shape)` you will see that there are two functions in the environment which enable you to call the methods defined in the impl block. You might think that this feel like an [R6 object](https://r6.r-lib.org/articles/Introduction.html) and you'd be right because an R6 object essentially is an environment! \n\n\n::: {.cell}\n\n```{.r .cell-code}\nas.list(Shape)\n#> $n_coords\n#> function () \n#> .Call(\"wrap__Shape__n_coords\", self, PACKAGE = \"rextendr1\")\n#> \n#> $new\n#> function (x) \n#> .Call(\"wrap__Shape__new\", x, PACKAGE = \"rextendr1\")\n```\n:::\n\n\nCalling the `new()` method instantiates a new enum variant. \n\n\n::: {.cell}\n\n```{.r .cell-code}\ntri <- Shape$new(\"triangle\")\ntri\n#> \n#> attr(,\"class\")\n#> [1] \"Shape\"\n```\n:::\n\n\nThe newly made `tri` object is an [external pointer](https://cran.r-project.org/doc/manuals/R-exts.html#External-pointers-and-weak-references) to the `Shape` enum in Rust. This pointer has the same methods as the Shape environment—though they cannot be seen in the same way. For example you can run the `n_coords()` method on the newly created object.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntri$n_coords()\n#> [1] 3\n```\n:::\n\n\n::: callout-tip\nTo make the methods visible to the `Shape` class you can define a `.DollarNames` method which will allow you to preview the methods and attributes when using the `$` syntax. This is very handy to define when making an impl a core part of your package.\n\n\n::: {.cell}\n\n```{.r .cell-code}\n.DollarNames.Shape = function(env, pattern = \"\") {\n ls(Shape, pattern = pattern)\n}\n```\n:::\n\n\n:::\n\n### `impl` ownership\n\nAdding the `#[extendr]` macro to an impl allows the struct or enum to be made available to R as an external pointer. Once you create an external pointer, that is then owned by R. So you can only get references to it or mutable references. If you need an owned version of the type, then you will need to clone it. \n\n## Accessing exported `impl`s from Rust\n\nInvariably, if you have made an impl available to R via the `#[extendr]` macro, you may want to define functions that take the impl as a function argument. \n\nDue to R owning the `impl`'s external pointer, these functions cannot take an owned version of the impl as an input. For example trying to define a function that subtracts an integer from the `n_coords()` output like below returns a compiler error.\n\n```rust\n#[extendr]\nfn subtract_coord(x: Shape, n: i32) -> i32 {\n (x.n_coords() as i32) - n\n}\n```\n```\nthe trait bound `Shape: extendr_api::FromRobj<'_>` is not satisfied\n --> src/lib.rs:53:22\n |\n | fn subtract_coord(x: Shape, n: i32) -> i32 {\n | ^^^^^ the trait `extendr_api::FromRobj<'_>` is not implemented for `Shape`\n |\nhelp: consider borrowing here\n |\n | fn subtract_coord(x: &Shape, n: i32) -> i32 {\n | +\n | fn subtract_coord(x: &mut Shape, n: i32) -> i32 {\n | ++++\n```\n\nAs most often, the compiler's suggestion is a good one. Use `&Shape` to use a reference.\n\n## `ExternalPtr`: returning arbitrary Rust types \n\nIn the event that you need to return a Rust type to R that doesn't have a compatible impl or is a type that you don't own, you can use `ExternalPtr`. The `ExternalPtr` struct allows any item to be captured as a pointer and returned to R. \n\nHere, for example, an `ExternalPtr` is returned from the `shape_ptr()` function.\n\n::: callout-tip\nAnything that is wrapped in `ExternalPtr` must implement the `Debug` trait.\n:::\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[derive(Debug)]\nenum Shape {\n Triangle,\n Rectangle,\n Pentagon,\n Hexagon,\n}\n\n#[extendr]\nfn shape_ptr(shape: &str) -> ExternalPtr {\n let variant = match shape {\n \"triangle\" => Shape::Triangle,\n \"rectangle\" => Shape::Rectangle,\n \"pentagon\" => Shape::Pentagon,\n \"hexagon\" => Shape::Hexagon,\n &_ => unimplemented!(),\n };\n\n ExternalPtr::new(variant)\n}\n```\n:::\n\n\nUsing an external pointer, however, is far more limiting than the `impl` block. For example, you cannot access and of its methods.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntri_ptr <- shape_ptr(\"triangle\")\ntri_ptr$n_coords()\n#> Error in tri_ptr$n_coords: object of type 'externalptr' is not subsettable\n```\n:::\n\n\nTo use an `ExternalPtr`, you have to go through a bit of extra work for it. \n\n\n\n\n\n```rust\n#[extendr]\nfn n_coords_ptr(x: Robj) -> i32 {\n let shape = TryInto::>::try_into(x); \n \n match shape {\n Ok(shp) => shp.n_coords() as i32,\n Err(_) => 0\n }\n}\n```\n\nThis function definition takes an `Robj` and from it, tries to create an `ExternalPtr`. Then, if the conversion did not error, it returns the number of coordinates as an i32 (R's version of an integer) and if there was an error converting, it returns 0.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntri_ptr <- shape_ptr(\"triangle\")\n\nn_coords_ptr(tri_ptr)\n#> [1] 3\n\nn_coords_ptr(list())\n#> [1] 0\n```\n:::\n\n\nFor a good example of using `ExternalPtr` within an R package, refer to the [`b64` R package](https://github.com/extendr/b64). \n", + "markdown": "---\ntitle: \"The extendr Macro\"\n---\n\n\nThe power of extendr is in its ability to use Rust from R. The `#[extendr]` macro is what determines what is exported to R from Rust. This section covers the basic usage of the `#[extendr]` macro. \n\n[`#[extendr]`](https://extendr.github.io/extendr/extendr_api/attr.extendr.html) is what is referred to as an [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros) (which itself is a type of [procedural macro](https://doc.rust-lang.org/reference/procedural-macros.html)). An attribute macro is attached to an [item](https://doc.rust-lang.org/reference/items.html) such as a function, `struct`, `enum`, or `impl`. \n\nThe `#[extendr]` attribute macro indicates that an item should be made available to R. However, it _can only be used_ with a function or an impl block. \n\n\n\n\n\n## Exporting functions \n\nIn order to make a function available to R, two things must happen. First, the `#[extendr]` macro must be attached to the function. For example, you can create a function `answer_to_life()`\n\n::: {.callout-note collapse=\"true\"}\nIn the Hitchhiker's Guide to the Galaxy, the number 42 is the answer to the universe. See this fun [article from Scientific American](https://www.scientificamerican.com/article/for-math-fans-a-hitchhikers-guide-to-the-number-42/)\n:::\n\n```rust\n#[extendr]\nfn answer_to_life() -> i32 {\n 42\n}\n```\n\nBy adding the `#[extendr]` attribute macro to the `answer_to_life()` function, we are indicating that this function has to be compatible with R. This alone, however, does not make the function available to R. It must be made available via the `extendr_module! {}` macro in `lib.rs`.\n\n```rust\nextendr_module! {\n mod hellorust;\n fn answer_to_life;\n}\n```\n\n::: callout-tip\nEverything that is made available in the `extendr_module! {}` macro in `lib.rs` must be compatible with R as indicated by the `#[extendr]` macro. Note that the module name `mod hellorust` must be the name of the R package that this is part of. If you have created your package with `rextendr::use_extendr()` this should be set automatically. See [Hello, world!](../r-pkgs/package-setup.qmd).\n:::\n\nWhat happens if you try and return something that cannot be represented by R? Take this example, an enum `Shape` is defined and a function takes a string `&str`. Based on the value of the arugment, an enum variant is returned. \n\n```rust\n#[derive(Debug)]\nenum Shape {\n Triangle,\n Rectangle,\n Pentagon,\n Hexagon,\n}\n\n#[extendr]\nfn make_shape(shape: &str) -> Shape {\n match shape {\n \"triangle\" => Shape::Triangle,\n \"rectangle\" => Shape::Rectangle,\n \"pentagon\" => Shape::Pentagon,\n \"hexagon\" => Shape::Hexagon,\n &_ => unimplemented!()\n }\n}\n```\n\nWhen this is compiled, an error occurs because extendr does not know how to convert the `Shape` enum into something that R can use. The error is fairly informative! \n\n```\n#[extendr]\n | ^^^^^^^^^^ the trait `ToVectorValue` is not implemented for `Shape`, which is required by `extendr_api::Robj: From`\n |\n = help: the following other types implement trait `ToVectorValue`:\n bool\n i8\n i16\n i32\n i64\n usize\n u8\n u16\n and 45 others\n = note: required for `extendr_api::Robj` to implement `From`\n = note: this error originates in the attribute macro `extendr` \n```\n\nIt tells you that `Shape` does not implement the `ToVectorValue` trait. The `ToVectorValue` trait is what enables items from Rust to be returned to R.\n\n## `ToVectorValue` trait\n\nIn order for an item to be returned from a function marked with the `#[extendr]` attribute macro, it must be able to be turned into an R object. In extendr, the struct `Robj` is a catch all for any type of R object. \n\n::: callout-note\nFor those familiar with PyO3, the `Robj` struct is similar in concept to the [`PyAny`](https://docs.rs/pyo3/latest/pyo3/types/struct.PyAny.html) struct.\n::: \n\nThe `ToVectorValue` trait is what is used to convert Rust items into R objects. The trait is implemented on a number of standard Rust types such as `i32`, `f64`, `usize`, `String` and more (see [all foreign implementations here](https://extendr.github.io/extendr/extendr_api/robj/into_robj/trait.ToVectorValue.html#foreign-impls)) which enables these functions to be returned from a Rust function marked with `#[extendr]`. \n\n::: callout-note\nIn essence, all items that are returned from a function must be able to be turned into an `Robj`. Other extendr types such as `List`, for example, have a `From for Robj` implementation that defines how it is converted into an `Robj`.\n:::\n\nThis means that with a little extra work, the `Shape` enum can be returned to R. To do so, the `#[extendr]` macro needs to be added to an impl block. \n\n\n## Exporting `impl` blocks\n\nThe other supported item that can be made available to R is an [`impl`](https://doc.rust-lang.org/std/keyword.impl.html) block. \n`impl` is a keyword that allows you to _implement_ a trait or an inherent implementation. The `#[extendr]` macro works with inherent implementations. These are `impl`s on a type such as an `enum` or a `struct`. extendr _does not_ support using `#[extendr]` on trait impls. \n\n::: callout-note\nYou can only add an inherent implementation on a type that you have own and not provided by a third party crate. This would violate the [orphan rules](https://github.com/Ixrec/rust-orphan-rules?tab=readme-ov-file#what-are-the-orphan-rules).\n:::\n\nContinuing with the `Shape` example, this enum alone cannot be returned to R. For example, the following code will result in a compilation error\n\n```rust\n#[derive(Debug)]\nenum Shape {\n Triangle,\n Rectangle,\n Pentagon,\n Hexagon,\n}\n\n#[extendr]\nfn make_shape(shape: &str) -> Shape {\n match shape {\n \"triangle\" => Shape::Triangle,\n \"rectangle\" => Shape::Rectangle,\n \"pentagon\" => Shape::Pentagon,\n \"hexagon\" => Shape::Hexagon,\n &_ => unimplemented!()\n }\n}\n```\n```\nerror[E0277]: the trait bound `Shape: ToVectorValue` is not satisfied\n --> src/lib.rs:19:1\n |\n19 | #[extendr]\n | ^^^^^^^^^^ the trait `ToVectorValue` is not implemented for `Shape`, which is required by `extendr_api::Robj: From`\n |\n```\n\nHowever, if an impl block is added to the `Shape` enum, it can be returned to R. \n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[derive(Debug)]\nenum Shape {\n Triangle,\n Rectangle,\n Pentagon,\n Hexagon,\n}\n\n#[extendr]\nimpl Shape {\n fn new(x: &str) -> Self {\n match x {\n \"triangle\" => Self::Triangle,\n \"rectangle\" => Self::Rectangle,\n \"pentagon\" => Self::Pentagon,\n \"hexagon\" => Self::Hexagon,\n &_ => unimplemented!(),\n }\n }\n\n fn n_coords(&self) -> usize {\n match &self {\n Shape::Triangle => 3,\n Shape::Rectangle => 4,\n Shape::Pentagon => 4,\n Shape::Hexagon => 5,\n }\n }\n}\n```\n:::\n\n\nIn this example two new methods are added to the `Shape` enum. The first `new()` is like the `make_shape()` function that was shown earlier: it takes a `&str` and returns an enum variant. Now that the enum has an `impl` block with `#[extendr]` attribute macro, it can be exported to R by inclusion in the `extendr_module! {}` macro.\n\n```rust\nextendr_module! {\n mod hellorust;\n impl Shape;\n}\n```\n\nDoing so creates an environment in your package called `Shape`. The environment contains all of the methods that are available to you. \n\n::: callout-tip\nThere are use cases where you may not want to expose any methods but do want to make it possible to return a struct or an enum to the R. You can do this by adding an empty impl block with the `#[extendr]` attribute macro. \n::: \n\nIf you run `as.list(Shape)` you will see that there are two functions in the environment which enable you to call the methods defined in the impl block. You might think that this feel like an [R6 object](https://r6.r-lib.org/articles/Introduction.html) and you'd be right because an R6 object essentially is an environment! \n\n\n::: {.cell}\n\n```{.r .cell-code}\nas.list(Shape)\n#> $n_coords\n#> function () \n#> .Call(\"wrap__Shape__n_coords\", self, PACKAGE = \"librextendr1.dylib\")\n#> \n#> $new\n#> function (x) \n#> .Call(\"wrap__Shape__new\", x, PACKAGE = \"librextendr1.dylib\")\n```\n:::\n\n\nCalling the `new()` method instantiates a new enum variant. \n\n\n::: {.cell}\n\n```{.r .cell-code}\ntri <- Shape$new(\"triangle\")\ntri\n#> \n#> attr(,\"class\")\n#> [1] \"Shape\"\n```\n:::\n\n\nThe newly made `tri` object is an [external pointer](https://cran.r-project.org/doc/manuals/R-exts.html#External-pointers-and-weak-references) to the `Shape` enum in Rust. This pointer has the same methods as the Shape environment—though they cannot be seen in the same way. For example you can run the `n_coords()` method on the newly created object.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntri$n_coords()\n#> [1] 3\n```\n:::\n\n\n::: callout-tip\nTo make the methods visible to the `Shape` class you can define a `.DollarNames` method which will allow you to preview the methods and attributes when using the `$` syntax. This is very handy to define when making an impl a core part of your package.\n\n\n::: {.cell}\n\n```{.r .cell-code}\n.DollarNames.Shape = function(env, pattern = \"\") {\n ls(Shape, pattern = pattern)\n}\n```\n:::\n\n\n:::\n\n### `impl` ownership\n\nAdding the `#[extendr]` macro to an impl allows the struct or enum to be made available to R as an external pointer. Once you create an external pointer, that is then owned by R. So you can only get references to it or mutable references. If you need an owned version of the type, then you will need to clone it. \n\n## Accessing exported `impl`s from Rust\n\nInvariably, if you have made an impl available to R via the `#[extendr]` macro, you may want to define functions that take the impl as a function argument. \n\nDue to R owning the `impl`'s external pointer, these functions cannot take an owned version of the impl as an input. For example trying to define a function that subtracts an integer from the `n_coords()` output like below returns a compiler error.\n\n```rust\n#[extendr]\nfn subtract_coord(x: Shape, n: i32) -> i32 {\n (x.n_coords() as i32) - n\n}\n```\n```\nthe trait bound `Shape: extendr_api::FromRobj<'_>` is not satisfied\n --> src/lib.rs:53:22\n |\n | fn subtract_coord(x: Shape, n: i32) -> i32 {\n | ^^^^^ the trait `extendr_api::FromRobj<'_>` is not implemented for `Shape`\n |\nhelp: consider borrowing here\n |\n | fn subtract_coord(x: &Shape, n: i32) -> i32 {\n | +\n | fn subtract_coord(x: &mut Shape, n: i32) -> i32 {\n | ++++\n```\n\nAs most often, the compiler's suggestion is a good one. Use `&Shape` to use a reference.\n\n## `ExternalPtr`: returning arbitrary Rust types \n\nIn the event that you need to return a Rust type to R that doesn't have a compatible impl or is a type that you don't own, you can use `ExternalPtr`. The `ExternalPtr` struct allows any item to be captured as a pointer and returned to R. \n\nHere, for example, an `ExternalPtr` is returned from the `shape_ptr()` function.\n\n::: callout-tip\nAnything that is wrapped in `ExternalPtr` must implement the `Debug` trait.\n:::\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[derive(Debug)]\nenum Shape {\n Triangle,\n Rectangle,\n Pentagon,\n Hexagon,\n}\n\n#[extendr]\nfn shape_ptr(shape: &str) -> ExternalPtr {\n let variant = match shape {\n \"triangle\" => Shape::Triangle,\n \"rectangle\" => Shape::Rectangle,\n \"pentagon\" => Shape::Pentagon,\n \"hexagon\" => Shape::Hexagon,\n &_ => unimplemented!(),\n };\n\n ExternalPtr::new(variant)\n}\n```\n:::\n\n\nUsing an external pointer, however, is far more limiting than the `impl` block. For example, you cannot access and of its methods.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntri_ptr <- shape_ptr(\"triangle\")\ntri_ptr$n_coords()\n#> Error in tri_ptr$n_coords: object of type 'externalptr' is not subsettable\n```\n:::\n\n\nTo use an `ExternalPtr`, you have to go through a bit of extra work for it. \n\n\n\n\n\n```rust\n#[extendr]\nfn n_coords_ptr(x: Robj) -> i32 {\n let shape = TryInto::>::try_into(x); \n \n match shape {\n Ok(shp) => shp.n_coords() as i32,\n Err(_) => 0\n }\n}\n```\n\nThis function definition takes an `Robj` and from it, tries to create an `ExternalPtr`. Then, if the conversion did not error, it returns the number of coordinates as an i32 (R's version of an integer) and if there was an error converting, it returns 0.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntri_ptr <- shape_ptr(\"triangle\")\n\nn_coords_ptr(tri_ptr)\n#> [1] 3\n\nn_coords_ptr(list())\n#> [1] 0\n```\n:::\n\n\nFor a good example of using `ExternalPtr` within an R package, refer to the [`b64` R package](https://github.com/extendr/b64). \n", "supporting": [], "filters": [ "rmarkdown/pagebreak.lua" diff --git a/_freeze/user-guide/type-mapping/scalars/execute-results/html.json b/_freeze/user-guide/type-mapping/scalars/execute-results/html.json index c495c82..b73b929 100644 --- a/_freeze/user-guide/type-mapping/scalars/execute-results/html.json +++ b/_freeze/user-guide/type-mapping/scalars/execute-results/html.json @@ -1,8 +1,8 @@ { - "hash": "af8f7e730143ad4599e3a14572806e65", + "hash": "7d123922be79f518188a370054bcf10c", "result": { "engine": "knitr", - "markdown": "---\ntitle: \"Scalar Type Mapping\"\n---\n\n::: {.cell}\n\n:::\n\n\nThis tutorial demonstrates some of the basics of passing scalar data types back\nand forth between Rust and R. We'll start with simple examples using explicit\nRust types but then move on to showing their extendr alternatives. Why does\nextendr have its own data types? For a number of reasons, of course, but the\nmost important reason is probably that Rust types do not allow for missing\nvalues, so no `NA`, `NaN`, `NULL`, or what have you. Fortunately, extendr types\nwill handle missing values for you. For this reason, **it is strongly\nrecommended that you work with the extendr types whenever possible.**\n\n## Scalar types\n\nA scalar type consists of a single value, and it can *only* consist of a single\nvalue, whether that value is a single character string, integer, or logical. As\nit happens, R doesn't have a way of representing a scalar value. That's because\neverything is a vector in R, and vectors can have any arbitrary length you want.\nSo, the closest thing to a scalar you will ever encounter in R is a vector that\njust so happens to have a length of one. In Rust, however, scalars are the\nbuilding blocks of everything, and they come in a bewildering variety, at least\nfor the traditional R user. Consider, for example, integers. R has just one way \nto represent this type of numeric value. Rust, on the other hand, has twelve!\n\nThe table below shows the most common R \"scalar\" types, along with their Rust\nand extendr equivalents. \n\n| R type | extendr type | Rust type |\n|----------------|--------------|----------------|\n| `integer(1)` | `Rint` | `i32` |\n| `double(1)` | `Rfloat` | `f64` |\n| `logical(1)` | `Rbool` | `bool` |\n| `complex(1)` | `Rcplx` | `Complex` |\n| `character(1)` | `Rstr` | `String` |\n\nTo learn more about Rust types, see [section 3.2 of The\nBook](https://doc.rust-lang.org/book/ch03-02-data-types.html).\n\n## Sharing scalars\n\nTo see how scalars get passed back and forth between Rust and R, we'll first\nexplore Rust's `f64` value which is a 64-bit float. This is equivalent to R's\n`double(1)`. We'll write a very simple Rust function that prints the value of\nthe input and does not return anything.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn scalar_double(x: f64) { \n rprintln!(\"The value of x is {x}\"); \n}\n```\n:::\n\n\nThrough the magic of extendr, we can now call this function in R and pass it a \nsingle double value.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nscalar_double(4.2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nThe value of x is 4.2\n```\n\n\n:::\n:::\n\n\nThere are several things to note about this example. First, in Rust, `x: f64` \ntells us that the type of `x` being passed to the function (`fn`) is a single \ndouble vector or \"float\" value. Second, `rprintln!(\"{}\", x);` is an extendr \nmacro that makes it easier to print information from Rust to the console in R. \nR users will perhaps notice that the syntax is vaguely `{glue}`-like in that the \nvalue of `x` is inserted into the curly brackets. Finally, if you are not \nworking inside of an extendr R package, you can create the `scalar_double()` \nfunction locally using `rextendr::rust_function()`.\n\n``` r\nrextendr::rust_function(\"\nfn scalar_double(x: f64) { \n rprintln!(\"The value of x is {x}\"); \n}\n\")\n```\n\nNow, what if, rather than printing the value of `x` to the R console, we wanted\ninstead to return that value to R? To do that, we just need to let Rust know\nwhat type is being returned by our function. This is done with the `-> type`\nnotation. The extendr crate understands this notation and knows how to handle \nthe scalar `f64` type returned by the Rust function and pass it to R as double.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn return_scalar_double(x: f64) -> f64 { \n x \n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- return_scalar_double(4.2)\n\ntypeof(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"double\"\n```\n\n\n:::\n\n```{.r .cell-code}\nx + 1.0\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 5.2\n```\n\n\n:::\n:::\n\n\n## Missing values\n\nAs noted above, Rust does not allow a scalar type to have a missing value, so \nyou cannot simply pass a missing value like `NA` to Rust and expect it to just \nwork. Here is a demonstration of this issue using a simple function which adds \n1.0 to `x`.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn plus_one(x: f64) -> f64 { \n x + 1.0 \n}\n```\n:::\n\n\nYou will notice that this function expects `x` to be `f64`, not a missing value.\nPassing a missing value from R to this Rust function will, therefore, result in \nan error.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nplus_one(NA_real_)\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in plus_one(NA_real_): Must not be NA.\n```\n\n\n:::\n:::\n\n\nFortunately, the extendr types are `NA`-aware, so you can, for instance, use \nextendr's `Rfloat` in place of `f64` to handle missing values without error. \nBelow, you will see that we have done this for the function `plus_one()`. \n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn plus_one(x: Rfloat) -> Rfloat { \n x + 1.0 \n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nplus_one(NA_real_)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] NA\n```\n\n\n:::\n\n```{.r .cell-code}\nplus_one(4.2)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 5.2\n```\n\n\n:::\n:::\n\n\n## Additional examples\n\nHere are additional examples showing how to pass scalars to Rust and return them \nto R using Rust scalar types.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn scalar_integer(x: i32) -> i32 { x }\n\n#[extendr]\nfn scalar_logical(x: bool) -> bool { x }\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nscalar_integer(4L)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 4\n```\n\n\n:::\n\n```{.r .cell-code}\nscalar_logical(TRUE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] TRUE\n```\n\n\n:::\n:::\n\n\nAnd here are the same examples with extendr scalar types.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn scalar_integer(x: Rint) -> Rint { x }\n\n#[extendr]\nfn scalar_logical(x: Rbool) -> Rbool { x }\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nscalar_integer(4L)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 4\n```\n\n\n:::\n\n```{.r .cell-code}\nscalar_logical(TRUE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] TRUE\n```\n\n\n:::\n:::\n\n\nDid you notice that we didn't give an example with character strings? Yeah, well,\nthere's a good reason for that. You can find out what that is by heading over to\nthe tutorial on [Handling Strings](user-guide/type-mapping/characters.qmd).", + "markdown": "---\ntitle: \"Scalar Type Mapping\"\n---\n\n::: {.cell}\n\n:::\n\n\nThis tutorial demonstrates some of the basics of passing scalar data types back\nand forth between Rust and R. We'll start with simple examples using explicit\nRust types but then move on to showing their extendr alternatives. Why does\nextendr have its own data types? For a number of reasons, of course, but the\nmost important reason is probably that Rust types do not allow for missing\nvalues, so no `NA`, `NaN`, `NULL`, or what have you. Fortunately, extendr types\nwill handle missing values for you. For this reason, **it is strongly\nrecommended that you work with the extendr types whenever possible.**\n\n## Scalar types\n\nA scalar type consists of a single value, and it can *only* consist of a single\nvalue, whether that value is a single character string, integer, or logical. As\nit happens, R doesn't have a way of representing a scalar value. That's because\neverything is a vector in R, and vectors can have any arbitrary length you want.\nSo, the closest thing to a scalar you will ever encounter in R is a vector that\njust so happens to have a length of one. In Rust, however, scalars are the\nbuilding blocks of everything, and they come in a bewildering variety, at least\nfor the traditional R user. Consider, for example, integers. R has just one way \nto represent this type of numeric value. Rust, on the other hand, has twelve!\n\nThe table below shows the most common R \"scalar\" types, along with their Rust\nand extendr equivalents. \n\n| R type | extendr type | Rust type |\n|----------------|--------------|----------------|\n| `integer(1)` | `Rint` | `i32` |\n| `double(1)` | `Rfloat` | `f64` |\n| `logical(1)` | `Rbool` | `bool` |\n| `complex(1)` | `Rcplx` | `Complex` |\n| `character(1)` | `Rstr` | `String` |\n\nTo learn more about Rust types, see [section 3.2 of The\nBook](https://doc.rust-lang.org/book/ch03-02-data-types.html).\n\n## Sharing scalars\n\nTo see how scalars get passed back and forth between Rust and R, we'll first\nexplore Rust's `f64` value which is a 64-bit float. This is equivalent to R's\n`double(1)`. We'll write a very simple Rust function that prints the value of\nthe input and does not return anything.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn scalar_double(x: f64) { \n rprintln!(\"The value of x is {x}\"); \n}\n```\n:::\n\n\nThrough the magic of extendr, we can now call this function in R and pass it a \nsingle double value.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nscalar_double(4.2)\n#> The value of x is 4.2\n```\n:::\n\n\nThere are several things to note about this example. First, in Rust, `x: f64` \ntells us that the type of `x` being passed to the function (`fn`) is a single \ndouble vector or \"float\" value. Second, `rprintln!(\"{}\", x);` is an extendr \nmacro that makes it easier to print information from Rust to the console in R. \nR users will perhaps notice that the syntax is vaguely `{glue}`-like in that the \nvalue of `x` is inserted into the curly brackets. Finally, if you are not \nworking inside of an extendr R package, you can create the `scalar_double()` \nfunction locally using `rextendr::rust_function()`.\n\n``` r\nrextendr::rust_function(\"\nfn scalar_double(x: f64) { \n rprintln!(\"The value of x is {x}\"); \n}\n\")\n```\n\nNow, what if, rather than printing the value of `x` to the R console, we wanted\ninstead to return that value to R? To do that, we just need to let Rust know\nwhat type is being returned by our function. This is done with the `-> type`\nnotation. The extendr crate understands this notation and knows how to handle \nthe scalar `f64` type returned by the Rust function and pass it to R as double.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn return_scalar_double(x: f64) -> f64 { \n x \n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- return_scalar_double(4.2)\n\ntypeof(x)\n#> [1] \"double\"\n\nx + 1.0\n#> [1] 5.2\n```\n:::\n\n\n## Missing values\n\nAs noted above, Rust does not allow a scalar type to have a missing value, so \nyou cannot simply pass a missing value like `NA` to Rust and expect it to just \nwork. Here is a demonstration of this issue using a simple function which adds \n1.0 to `x`.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn plus_one(x: f64) -> f64 { \n x + 1.0 \n}\n```\n:::\n\n\nYou will notice that this function expects `x` to be `f64`, not a missing value.\nPassing a missing value from R to this Rust function will, therefore, result in \nan error.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nplus_one(NA_real_)\n#> Error in plus_one(NA_real_): Must not be NA.\n```\n:::\n\n\nFortunately, the extendr types are `NA`-aware, so you can, for instance, use \nextendr's `Rfloat` in place of `f64` to handle missing values without error. \nBelow, you will see that we have done this for the function `plus_one()`. \n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn plus_one(x: Rfloat) -> Rfloat { \n x + 1.0 \n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nplus_one(NA_real_)\n#> [1] NA\n\nplus_one(4.2)\n#> [1] 5.2\n```\n:::\n\n\n## Additional examples\n\nHere are some additional examples showing how to pass scalars to Rust and return them \nto R using Rust scalar types.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn scalar_integer(x: i32) -> i32 { x }\n\n#[extendr]\nfn scalar_logical(x: bool) -> bool { x }\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nscalar_integer(4L)\n#> [1] 4\n\nscalar_logical(TRUE)\n#> [1] TRUE\n```\n:::\n\n\nAnd here are the same examples with extendr scalar types.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn scalar_integer(x: Rint) -> Rint { x }\n\n#[extendr]\nfn scalar_logical(x: Rbool) -> Rbool { x }\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nscalar_integer(4L)\n#> [1] 4\n\nscalar_logical(TRUE)\n#> [1] TRUE\n```\n:::\n\n\nDid you notice that we didn't give an example with character strings? Yeah, well,\nthere's a good reason for that. You can find out what that is by heading over to\nthe tutorial on [Handling Strings](user-guide/type-mapping/characters.qmd).", "supporting": [], "filters": [ "rmarkdown/pagebreak.lua" diff --git a/_freeze/user-guide/type-mapping/vectors/execute-results/html.json b/_freeze/user-guide/type-mapping/vectors/execute-results/html.json index 7beae4a..64454de 100644 --- a/_freeze/user-guide/type-mapping/vectors/execute-results/html.json +++ b/_freeze/user-guide/type-mapping/vectors/execute-results/html.json @@ -2,7 +2,7 @@ "hash": "f1c04cd553e224259a06a758fee2eb97", "result": { "engine": "knitr", - "markdown": "---\ntitle: \"Vector Type Mapping\"\n---\n\n::: {.cell}\n\n:::\n\n\n## Vector Type Mapping with Rust Types\n\nWhat happens if we try to pass more than one value to `scalar_double()`?\n\n\n::: {.cell}\n\n```{.r .cell-code}\nscalar_double(c(4.2, 1.3, 2.5))\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in scalar_double(c(4.2, 1.3, 2.5)): could not find function \"scalar_double\"\n```\n\n\n:::\n:::\n\n\nIt errors because the function expects a scalar of the `f64` type, not a vector\nof `f64`.\n\nIn this section, we show you how to pass Rust vectors between R and Rust.\n\n::: callout-important\nWhile using a Rust vector is possible in some cases, it is strongly not\nrecommended. Instead, extendr types should be used as they provide access\ndirectly to R objectes. Whereas using Rust vectors requires additional\nallocations.\n:::\n\nThe syntax is basically the same as with scalars, with just some minor changes.\nWe'll use doubles again to demonstrate this.\n\nFor reference, below are the type of Rust vectors that can be utilized with\nextendr.\n\n| R type | extendr type | Rust type |\n|---------------|--------------|---------------------|\n| `integer()` | `Integers` | `Vec` |\n| `double()` | `Doubles` | `Vec` |\n| `complex()` | `Complexes` | `Vec>` |\n| `character()` | `Strings` | `Vec` |\n| `raw()` | `Raw` | `&[u8]` |\n| `logical()` | `Logicals` | |\n| `list()` | `List` | |\n\n::: callout-note\nYou might have anticipated `Vec` to be a supported Rust vector type. This\nis not possible because in R, logical vectors do not contain *only* `true` and\n`false` like Rust's bool type. They also can be an `NA` value which has no\ncorresponding representation in Rust.\n:::\n\nBelow defines Rust function which takes in a vector of `f64` values and prints\nthem out.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn vector_double(x: Vec) {\n rprintln!(\"The values of x are {x:?}\");\n}\n```\n:::\n\n\nThat function can be called from R which prints the Debug format of the vector.\n\n::: callout-tip\nRust's vector do not implement the\n[Display](https://doc.rust-lang.org/std/fmt/trait.Display.html) trait so the\ndebug format (`:?`) is used.\n:::\n\n\n::: {.cell}\n\n```{.r .cell-code}\nvector_double(c(4.2, 1.3, 2.5))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\nThe values of x are [4.2, 1.3, 2.5]\n```\n\n\n:::\n:::\n\n\nReturning values using Rust follows the same rules as R. You do not need to\nexplicitly return a value as long as the last item in an expression is not\nfollowed by a `;`.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn vector_double(x: Vec) -> Vec { \n x \n}\n```\n:::\n\n\nCalling the function returns the input as a double vector\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- vector_double(c(4.2, 1.3, 2.5))\ntypeof(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"double\"\n```\n\n\n:::\n\n```{.r .cell-code}\nx + 1\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 5.2 2.3 3.5\n```\n\n\n:::\n:::\n\n\n### Additional examples\n\nThese same principles can be extended to other supported vector types such as\n`Vec` and `Vec`.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn vector_integer(x: Vec) -> Vec { \n x\n}\n\n#[extendr]\nfn vector_character(x: Vec) -> Vec {\n x \n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nvector_integer(c(4L, 6L, 8L))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 4 6 8\n```\n\n\n:::\n\n```{.r .cell-code}\nvector_character(c(\"Hello world!\", \"Hello extendr!\", \"Hello R!\"))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"Hello world!\" \"Hello extendr!\" \"Hello R!\" \n```\n\n\n:::\n:::", + "markdown": "---\ntitle: \"Vector Type Mapping\"\n---\n\n::: {.cell}\n\n:::\n\n\n## Vector Type Mapping with Rust Types\n\nWhat happens if we try to pass more than one value to `scalar_double()`?\n\n\n::: {.cell}\n\n```{.r .cell-code}\nscalar_double(c(4.2, 1.3, 2.5))\n#> Error in scalar_double(c(4.2, 1.3, 2.5)): could not find function \"scalar_double\"\n```\n:::\n\n\nIt errors because the function expects a scalar of the `f64` type, not a vector\nof `f64`.\n\nIn this section, we show you how to pass Rust vectors between R and Rust.\n\n::: callout-important\nWhile using a Rust vector is possible in some cases, it is strongly not\nrecommended. Instead, extendr types should be used as they provide access\ndirectly to R objectes. Whereas using Rust vectors requires additional\nallocations.\n:::\n\nThe syntax is basically the same as with scalars, with just some minor changes.\nWe'll use doubles again to demonstrate this.\n\nFor reference, below are the type of Rust vectors that can be utilized with\nextendr.\n\n| R type | extendr type | Rust type |\n|---------------|--------------|---------------------|\n| `integer()` | `Integers` | `Vec` |\n| `double()` | `Doubles` | `Vec` |\n| `complex()` | `Complexes` | `Vec>` |\n| `character()` | `Strings` | `Vec` |\n| `raw()` | `Raw` | `&[u8]` |\n| `logical()` | `Logicals` | |\n| `list()` | `List` | |\n\n::: callout-note\nYou might have anticipated `Vec` to be a supported Rust vector type. This\nis not possible because in R, logical vectors do not contain *only* `true` and\n`false` like Rust's bool type. They also can be an `NA` value which has no\ncorresponding representation in Rust.\n:::\n\nBelow defines Rust function which takes in a vector of `f64` values and prints\nthem out.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn vector_double(x: Vec) {\n rprintln!(\"The values of x are {x:?}\");\n}\n```\n:::\n\n\nThat function can be called from R which prints the Debug format of the vector.\n\n::: callout-tip\nRust's vector do not implement the\n[Display](https://doc.rust-lang.org/std/fmt/trait.Display.html) trait so the\ndebug format (`:?`) is used.\n:::\n\n\n::: {.cell}\n\n```{.r .cell-code}\nvector_double(c(4.2, 1.3, 2.5))\n#> The values of x are [4.2, 1.3, 2.5]\n```\n:::\n\n\nReturning values using Rust follows the same rules as R. You do not need to\nexplicitly return a value as long as the last item in an expression is not\nfollowed by a `;`.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn vector_double(x: Vec) -> Vec { \n x \n}\n```\n:::\n\n\nCalling the function returns the input as a double vector\n\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- vector_double(c(4.2, 1.3, 2.5))\ntypeof(x)\n#> [1] \"double\"\nx + 1\n#> [1] 5.2 2.3 3.5\n```\n:::\n\n\n### Additional examples\n\nThese same principles can be extended to other supported vector types such as\n`Vec` and `Vec`.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn vector_integer(x: Vec) -> Vec { \n x\n}\n\n#[extendr]\nfn vector_character(x: Vec) -> Vec {\n x \n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nvector_integer(c(4L, 6L, 8L))\n#> [1] 4 6 8\n\nvector_character(c(\"Hello world!\", \"Hello extendr!\", \"Hello R!\"))\n#> [1] \"Hello world!\" \"Hello extendr!\" \"Hello R!\"\n```\n:::", "supporting": [], "filters": [ "rmarkdown/pagebreak.lua" diff --git a/user-guide/_drafts/conversion.qmd b/user-guide/_drafts/conversion.qmd index 9fe8701..657742e 100644 --- a/user-guide/_drafts/conversion.qmd +++ b/user-guide/_drafts/conversion.qmd @@ -15,7 +15,7 @@ library(rextendr) A fundamental data-type in R is the 32-bit integer, `int` in C, and `i32` in Rust. Passing that type around is essential, and straight forward: ```{extendrsrc} -#[extendr(use_try_from = true)] +#[extendr] fn ultimate_answer() -> i32 { return 42_i32; } @@ -26,7 +26,7 @@ And now this function is available within your R-session, as the output is `r ul Also, another fundamental data-type in R is `numeric` / `f64`, which we can also pass back and forth uninhibitated, e.g. ```{extendrsrc} -#[extendr(use_try_from = true)] +#[extendr] fn return_tau() -> f64 { std::f64::consts::TAU } @@ -37,7 +37,7 @@ where $\tau := 2\pi =$ $`r return_tau()`$. However, passing data from R to Rust must be done with a bit of care: In R, representing a true integer in literal form requires using `L` after the literal. ```{extendrsrc} -#[extendr(use_try_from = true)] +#[extendr] fn bit_left_shift_once(number: i32) -> i32 { number << 1 } @@ -61,7 +61,7 @@ bit_left_shift_once(NA) Instead, we have to rely on extendr's scalar variants of R types, `Rint` / `Rfloat` to encompass the notion of `NA` in our functions: ```{extendrsrc} -#[extendr(use_try_from = true)] +#[extendr] fn double_me(value: Rint) -> Rint { if value.is_na() { Rint::na() @@ -83,10 +83,9 @@ for this, is when you specify a type without `&`/`&mut`, the value is coerced in a similar way, as R coerces values. In order to have strict type-checking during run-time, use `&` / `&mut`, as - ```{extendrsrc} -#[extendr(use_try_from = true)] -fn wrong_input(value: &Rint) -> Rint { +#[extendr] +fn wrong_input(value: Rint) -> Rint { value.clone() } ``` @@ -111,7 +110,7 @@ is exceeded, will there be a reallocation. Naively, we may define a function like so ```{extendrsrc} -#[extendr(use_try_from = true)] +#[extendr] fn repeat_us(mut values: Vec) -> Vec { assert_eq!(values.capacity(), values.len(), "must have zero capacity left"); values[0] = 100; @@ -131,7 +130,7 @@ converted to a Rust owned type, and it is that type that we can modify, and augm Of course, a slice e.g. `&[i32]` / `&mut [i32]` could be used instead, and this allows us to modify the original data, i.e. ```{extendrsrc} -#[extendr(use_try_from = true)] +#[extendr] fn zero_middle_element(values: &mut [i32]) { let len = values.len(); let middle = len / 2;