diff --git a/_freeze/changelog/execute-results/html.json b/_freeze/changelog/execute-results/html.json index c3db5f6..2710a65 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- 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", + "markdown": "# Changelog\n\n## Unreleased\n\n### Added\n\n### Changed\n\n### Fixed\n\n### Deprecated\n\n## 0.7.0\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` [[#730]](https://github.com/extendr/extendr/pull/730)\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 [[#600]](https://github.com/extendr/extendr/pull/600)\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 e5f44ac..2210ec8 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#> [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] \"Elit inceptos bibendum vulputate velit lacus nibh sociis placerat vitae maecenas vehicula varius sapien ultricies praesent eros aliquet fermentum malesuada suspendisse!\" \n#> [2] \"Elit tristique mollis mollis placerat nec sapien fermentum nisl mauris sed sagittis facilisis nisl ultricies orci scelerisque sociis aenean viverra velit dictum iaculis congue tellus euismod laoreet ultrices.\"\n#> [3] \"Amet hac tellus nisi vestibulum dignissim cum tempor senectus himenaeos vivamus hac dui felis rutrum est dictum at proin facilisi nostra fermentum velit pharetra erat turpis ultrices nisl sed aptent at?\" \n#> [4] \"Amet sem erat ac luctus aliquet at leo imperdiet velit diam maecenas ultrices venenatis convallis magna malesuada cum risus nascetur primis orci imperdiet maecenas ridiculus libero mi fusce porttitor lectus.\" \n#> [5] \"Amet justo sagittis conubia phasellus tristique vitae egestas fusce lectus phasellus maecenas nisi blandit condimentum placerat risus odio vel sed convallis dapibus molestie malesuada sagittis suscipit purus.\"\n#> [6] \"Lorem mattis donec himenaeos felis proin diam faucibus habitant per dapibus lacus lacinia potenti aliquam porttitor eu nulla!\"\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.7ms 13.8ms 70.2 1.16MB 0 \n#> 2 snakecase 205.3ms 217.7ms 4.63 12.24MB 7.72\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] \"Adipiscing fusce dui habitasse porta libero blandit massa suscipit vulputate vel facilisis lobortis euismod lacinia dictum id ullamcorper faucibus vulputate viverra integer aenean augue curae purus.\" \n#> [2] \"Adipiscing morbi magnis mi ultrices taciti ullamcorper nascetur sociis volutpat nulla iaculis urna tristique in massa auctor semper cubilia luctus erat vivamus vestibulum lectus varius integer lacinia?\" \n#> [3] \"Lorem nisi et velit ornare nam odio rhoncus vulputate scelerisque est convallis enim habitasse inceptos praesent leo taciti nullam sagittis himenaeos natoque sapien praesent cras.\" \n#> [4] \"Consectetur auctor neque aliquet id vivamus varius cum tempor ac duis potenti tempus dui enim ad netus magna facilisi venenatis curabitur quisque pharetra cras tristique platea.\" \n#> [5] \"Sit rutrum facilisis pharetra velit sem primis per nec et iaculis sociosqu duis a praesent justo nec netus faucibus in non hac magnis dapibus tempor in per iaculis?\" \n#> [6] \"Ipsum nullam dis risus vehicula ante donec varius risus fermentum tempor fermentum litora mi litora est magna sem porttitor morbi hendrerit taciti arcu ultricies est arcu porttitor mattis cum vitae aliquam.\"\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.9ms 13.9ms 71.2 1.16MB 0 \n#> 2 snakecase 206.4ms 207.5ms 4.75 12.27MB 7.92\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 5950c3d..edc6233 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 @@ -2,7 +2,7 @@ "hash": "22654123c43388ff150f0302fabc178e", "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!](../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```rust\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 any 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```rust\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 any 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"