Minimal Examples of Using Rust Code in R
Rust is a modern alternative to C and compiled rust code is ABI compatible with C. Many Rust libraries include C API headers so that the compiled rust code can be called from R/C/C as if it were C code. This package shows how to do this. The r-rust organization contains several more simple R packages interfacing with cargo crates.
To learn more about using Rust code in R packages, also have a look at the r-rust FAQ and the slides about this project presented at eRum2018!
Bundle your rust code in a the embedded cargo package (see the Cargo.toml
file) and then the src/Makevars file is written such that R will automatically build the rust modules when the R package is installed.
hellorust
├─ configure ← checks if 'cargo' is installed
├─ src
│ ├─ myrustlib ← bundled cargo package with your code
│ | ├─ Cargo.toml ← cargo dependencies and metadata
│ | ├─ src ← rust source code
│ | └─ api.h ← C headers for exported rust API
| |
│ ├─ Makevars ← Ties everything together
│ └─ wrapper.c ← C code for R package
├─ DESCRIPTION
└─ R ← Standard R C stuff
As per the new 2023 cran guidelines we now vendor the cargo crates in the R source packages in order to support offline installation. This is done in a two step process:
- (by package author) The vendor-update.sh script creates the
vendor.tar.xz
bundle that contains all the cargo sources. In addition, the vendor-authors.R script generates aninst/AUTHORS
file that lists the authors of the dependencies, as required by CRAN. Both of these scripts are called in the package cleanup file and therefore run automatically duringR CMD build
when the source package is created. - (by the user) At install time, the Makevars extracts the
vendor.tar.xz
bundle (when available) and generates a.cargo/config.toml
file to instructcargo build
to use the vendored (offline) sources.
If you run R CMD INSTALL
directly from a checkout (without building a source package), then no vendor.tar.xz
is created and cargo falls back to downloading crates on-the-fly.
You can test or force the use of vendored sources by passing --offline
to cargo build
.
If Rust is available, clone this repository and run the regular R CMD INSTALL
command:
R CMD INSTALL hellorust
Alternatively, to download and install from within R itself:
# install.packages("remotes")
remotes::install_github("r-rust/hellorust")
The standard rust toolchain includes a great package manager cargo
with a corresponding registry crates.io. Cargo makes it very easy to build a rust package including all dependencies into a static library that can easily be linked into an R package.
This is perfect for R because we can compile and link all rust code at build-time without any system dependencies. Rust itself has no substantial runtime so the resulting R package is entirely self contained. Indeed, rust has been designed specifically to serve well as an embedded language.
Note that cargo
is only needed at build-time. Rust has no runtime dependencies. The easiest way to install the latest version of Rust (including cargo) is from: https://www.rust-lang.org/tools/install
Alternatively, you may install cargo from your OS package manager:
- Debian/Ubuntu:
sudo apt-get install cargo
- Fedora/CentOS*:
sudo yum install cargo
- MacOS:
brew install rustc
*Note that on CentOS you first need to enable EPEL via sudo yum install epel-release
.
In order for rust to work with R you need to install the toolchain using rustup
and then add the x86_64-pc-windows-gnu
target. First download rustup-init.exe and then install the default toolchain:
rustup-init.exe -y --default-host x86_64-pc-windows-gnu
Or if rust is already installed (for example on GitHub actions), you can simply add the target:
rustup target add x86_64-pc-windows-gnu
To compile 32bit packages also add the i686-pc-windows-gnu
target, but 32-bit is no longer supported as of R 4.2.
Update 2023: This step is no longer needed because GitHub action runners now have the required Rust targets preinstalled by default.
To use GitHub actions, you can use the standard r workflow script in combination with this extra step:
- name: Add Rtools targets to Rust
if: runner.os == 'Windows'
run: |
rustup target add i686-pc-windows-gnu
rustup target add x86_64-pc-windows-gnu
The gifski package has been on CRAN since 2018, and uses this same structure.
- r-rust FAQ
- Erum2018 slides about this project presented by Jeroen
- Rust Inside Other Languages chapter from official rust documentation
- extendr: a more advanced R extension interface using Rust
- Duncan's proof of concept: RCallRust