---
title: "Using Rust code in R packages"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Using Rust code in R packages}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

The `rextendr` package supports R package development by building the 
scaffolding necessary to use extendr. This is done by calling `rextendr::use_extendr()`, 
which should feel familiar to anyone who has worked with `usethis::use_cpp11()`. 
As with `devtools`, it is not required to add `rextendr` to the `Depends` or 
`Imports` of the package `DESCRIPTION`.

The development workflow is designed to be as consistent with any R extension 
workflow using `devtools`. The whole process can be summarized this way:

1. Initialize a package with `usethis::create_package()` and `rextendr::use_extendr()`. 
2. Compile a package with `devtools::document()`.
3. Load a package with `devtools::load_all()`.

The following vignette walks through this workflow in more detail, highlighting
important features of the r/extendr scaffolding and build process along the way.

## Initialize a package

As an example workflow, let's pick `myextendr` as the name of our extendr-powered 
R package. The first step in development is to create an empty R package 
directory with `usethis::create_package("path/to/myextendr")`. Then, we simply 
execute `rextendr::use_extendr()` inside that package directory to generate the 
required extendr scaffolding.

``` r
rextendr::use_extendr()
#> ✔ Writing 'src/entrypoint.c'
#> ✔ Writing 'src/Makevars.in'
#> ✔ Writing 'src/Makevars.win.in'
#> ✔ Writing 'cleanup'
#> ✔ Writing 'cleanup.win'
#> ✔ Writing 'src/.gitignore'
#> ✔ Writing 'src/rust/Cargo.toml'
#> ✔ Writing 'src/rust/src/lib.rs'
#> ✔ Writing 'src/testpkg-win.def'
#> ✔ Writing 'src/rust/document.rs'
#> ✔ File 'R/extendr-wrappers.R' already exists. Skip writing the file.
#> ✔ Writing 'tools/msrv.R'
#> ✔ Writing 'tools/config.R'
#> ✔ Writing 'configure'
#> ✔ Writing 'configure.win'
#> ✔ Finished configuring extendr for package testpkg.
#> * Please run `devtools::document()` for changes to take effect.
#> i Call `use_extendr_badge()` to add an extendr badge to your 'README'
```

For developers who use RStudio, we also provide a project template that will 
call `usethis::create_package()` and `rextendr::use_extendr()` for you. This is 
done using RStudio's *Create Project* command, which you can find on the 
global toolbar or in the File menu. Choose "New Directory" then select "R 
package with extendr." You can then fill out the details to match your 
preferences.

Once you have the project directory setup, we strongly encourage you to run 
`rextendr::rust_sitrep()` in the console. This will provide a detailed report of 
the current state of your Rust infrastructure, along with some helpful advice 
about how to address any issues that may arise.

Assuming we have a proper installation of Rust, we are just one step away from 
calling Rust functions from R. As the message above says, we need to run 
`devtools::document()`. Before doing that, however, let's look at the 
scaffolding files that were added to our package directory.

### Package structure

Calling `rextendr::use_extendr()` generates the following scaffolding:

```
.
├── R
│   └── extendr-wrappers.R
...
└── src
    ├── Makevars
    ├── Makevars.win
    ├── entrypoint.c
    └── rust
        ├── Cargo.toml
        ├── document.rs
        └── src
            └── lib.rs
```

* **`R/extendr-wrappers.R`**: This file contains auto-generated R functions from 
  Rust code. We don't modify this file by hand.
* **`src/Makevars`**, **`src/Makevars.win`**: These files ensure that 
  `cargo build --lib` and `cargo run --bin document` get called during the 
  installation of the R package, ensuring that the crate library is compiled and
  the R wrappers generated. In most cases, we don't edit these by hand.
* **`src/entrypoint.c`**: This file is needed to avoid the linker removing the 
  static library. In 99.9% of cases, we don't edit this (except for changing the 
  crate name).
* **`src/rust/`**: Rust code of a crate using extendr-api. This is where we 
  mainly write code.

Two files in `src/rust` deserve some further consideration:

`src/rust/Cargo.toml`

``` toml
[package]
name = 'myextendr'
publish = false
version = '0.1.0'
edition = '2021'
rust-version = '1.65'

[lib]
crate-type = [ 'rlib', 'staticlib' ]
name = 'myextendr'

[[bin]]
name = 'document'
path = 'document.rs'
bench = false

[dependencies]
extendr-api = '*'

[profile.release]
lto = true
codegen-units = 1
```

The crate name is the same name as the R package's name by default. You can 
change this, but it might be a bit cumbersome to tweak other files accordingly, 
so we recommend leaving it as is. You will also probably want to specify a 
concrete extendr version, for example `extendr-api = '0.2'`. To try the 
development version of the extendr, you can modify the dependency to read

``` toml
extendr-api = { git = 'https://github.com/extendr/extendr' }
```

Now let us look at the main Rust library script.

`src/rust/src/lib.rs`

``` rs
use extendr_api::prelude::*;

/// Return string `"Hello world!"` to R.
/// @export
#[extendr]
fn hello_world() -> &'static str {
    "Hello world!"
}

// Macro to generate exports.
// This ensures exported functions are registered with R.
// See corresponding C code in `entrypoint.c`.
extendr_module! {
    mod myextendr;
    fn hello_world;
}
```

There are a few things to note about this file. First, the `use` statement 
brings commonly used extendr API functions into the current scope. Second, the 
three forward slashes `///` indicate a Rust [document comment](https://doc.rust-lang.org/reference/comments.html#doc-comments),
which is used to generate a crate's documentation. In extendr, these lines are
copied to the auto-generated R code as roxygen comments. This is analogous to
Rcpp/cpp11's `//'`. Finally, the `#[extendr]` and `extendr_module!` macros 
ensure that corresponding R functions are generated automatically, similar to 
how Rcpp's `[[Rcpp::export]]` and cpp11's `[[cpp11::register]]` work. Note that 
it is never sufficient to add the `#[extendr]` macro above a function 
definition. Those function names must also be collected in `extendr_module!` to 
generate the necessary wrappers.

## Compile a package

Compiling Rust code into R functions is as easy as calling `devtools::document()`, 
just as we would do if writing a package around C or C++. The documentation 
process first compiles the Rust library, then generates R wrappers along with 
their documentation if provided, and updates the NAMESPACE. The whole process 
thus leads to several files being either updated or generated from scratch:

```
.
...
├── NAMESPACE                 ----------(4)
├── R
│   └── extendr-wrappers.R    ----------(3)
├── man
│   └── hello_world.Rd        ----------(4)
└── src
    ├── myextendr.so          ----------(2)
    └── rust
        └── target
            └── release
                ├── libmyextendr.a   ---(1)
                ...
```

1. **`src/rust/target/release/libmyextendr.a`** (the extension depends on the OS): 
   This is the static library built from Rust code. This will be then used for 
   compiling the shared object `myextendr.so`.
2. **`src/myextendr.so`** (the extension depends on the OS): This is the shared 
   object that is actually called from R.
3. **`R/extendr-wrappers.R`**: The auto-generated R functions, including roxygen
   comments, go into this file. The roxygen comments are accordingly converted 
   into Rd files and `NAMESPACE`.
4. **`man/`**, **`NAMESPACE`**: These are generated from roxygen comments.

### Generated R code

While we never edit the R code in `R/extendr-wrappers.R` by hand, it might be 
good to know what that file looks like. For our default hello-world library, it 
is this:

``` r
# Generated by extendr: Do not edit by hand
#
#' @usage NULL
#' @useDynLib testPackage, .registration = TRUE
NULL

#' Return string `"Hello world!"` to R.
#' @export
hello_world <- function() .Call(wrap__hello_world)
```

The Roxygen directive `@useDynLib testPackage, .registration = TRUE` ensures 
that `useDynLib(myextendr, .registration = TRUE)` is added to the `NAMESPACE`,
which allows for calling the compiled Rust code in R. We also see that the 
roxygen comments from the Rust script are copied here. 

To help clarify this compilation process, let's implement a new Rust function. 
First, we add the function with `@export`, so it will get exported from the 
generated R package. This is followed by the `#[extendr]` macro above the 
function definition, with the function name then added to `extendr_module!`.

``` rs
/// @export
#[extendr]
fn add(x: i32, y: i32) -> i32 {
    x + y
}

extendr_module! {
    mod myextendr;
    fn hello_world;
    fn add;
}
```

After we re-build the package with `devtools::document()`, you should see the 
new `add` function in `R/extendr-wrappers.R`:

```r 
#' @export
add <- function(x, y) .Call(wrap__add, x, y)
```

## Load a package

Currently, our R package has two Rust-powered functions, `hello_world()` and 
`add()`. In a development workflow, we would access these functions in the 
current R session by simply loading the package with `devtools::load_all()`.

``` r
devtools::load_all(".")

hello_world()
#> [1] "Hello world!"

add(1L, 2L)
#> [1] 3
```

Alternatively, we could install the package with `devtools::install()`, then 
attach it with `library()`. In either case, we are now free to test our 
functions interactively.
