| Title: | Build 'Rust' Powered 'R' Packages |
|---|---|
| Description: | Provides a framework for creating high-performance 'R' packages powered by the 'Rust' programming language using the 'extendr' Rust crate. It offers 'usethis'-like functions to scaffold and develop 'Rust' powered 'R' packages, including utilities for publishing to CRAN, managing dependencies, configuring development environments, and rendering 'Rust' code in 'knitr' documents. Additionally, it provides functions to compile and evaluate 'Rust' code directly from 'R' for interactive development. |
| Authors: | Claus O. Wilke [aut] (ORCID: <https://orcid.org/0000-0002-7470-9261>), Andy Thomason [aut], Mossa M. Reimert [aut], Ilia Kosenkov [aut] (ORCID: <https://orcid.org/0000-0001-5563-7840>), Malcolm Barrett [aut] (ORCID: <https://orcid.org/0000-0003-0299-5825>), Josiah Parry [aut] (ORCID: <https://orcid.org/0000-0001-9910-865X>), Kenneth Vernon [aut, cre] (ORCID: <https://orcid.org/0000-0003-0098-5092>), Alberson Miranda [ctb] (ORCID: <https://orcid.org/0000-0001-9252-4175>) |
| Maintainer: | Kenneth Vernon <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.5.0 |
| Built: | 2026-05-18 15:24:06 UTC |
| Source: | https://github.com/extendr/rextendr |
Removes Rust binaries (such as .dll/.so libraries), C wrapper object files,
invokes cargo clean to reset cargo target directory
(found by default at pkg_root/src/rust/target/).
Useful when Rust code should be recompiled from scratch.
clean(path = ".", echo = TRUE)clean(path = ".", echo = TRUE)
path |
character scalar, path to R package root. |
echo |
logical scalar, should cargo command and outputs be printed to
console (default is |
character vector with names of all deleted files (invisibly).
## Not run: clean() ## End(Not run)## Not run: clean() ## End(Not run)
R packages developed using extendr are not immediately ready to be published to CRAN. The extendr package template ensures that CRAN publication is (farily) painless.
In order to publish a Rust based package on CRAN it must meet certain requirements. These are:
Rust dependencies are vendored
The package is compiled offline
the DESCRIPTION file's SystemRequirements field contains Cargo (Rust's package manager), rustc
The extendr templates handle all of this except vendoring dependencies.
This must be done prior to publication using vendor_crates().
In addition, it is important to make sure that CRAN maintainers
are aware that the package they are checking contains Rust code.
Depending on which and how many crates are used as a dependencies
the vendor.tar.xz will be larger than a few megabytes. If a
built package is larger than 5mbs CRAN may reject the submission.
To prevent rejection make a note in your cran-comments.md file
(create one using usethis::use_cran_comments()) along the lines of
"The package tarball is 6mb because Rust dependencies are vendored within src/rust/vendor.tar.xz which is 5.9mb."
As of rextendr 0.4.0, this function is no longer strictly necessary.
Packages created with use_extendr() now include a document binary that
generates R/extendr-wrappers.R as part of the normal cargo build step,
so devtools::document() works directly without any rextendr-specific
pre-processing. rextendr::document() is retained for backwards
compatibility.
rextendr::document() updates the package documentation for an R package
that uses extendr code. It is a wrapper for devtools::document().
document(pkg = ".", quiet = FALSE, roclets = NULL)document(pkg = ".", quiet = FALSE, roclets = NULL)
pkg |
The package to use, can be a file path to the package or a
package object. See |
quiet |
if |
roclets |
Character vector of roclet names to use with package.
The default, |
No return value, called for side effects.
Two knitr engines that enable code chunks of type extendr (individual Rust
statements to be evaluated via rust_eval()) and extendrsrc (Rust functions
or classes that will be exported to R via rust_source()).
eng_extendr(options) eng_extendrsrc(options)eng_extendr(options) eng_extendrsrc(options)
options |
A list of chunk options. |
A character string representing the engine output.
Retrieve metadata for packages and workspaces
read_cargo_metadata(path = ".", dependencies = FALSE, echo = FALSE)read_cargo_metadata(path = ".", dependencies = FALSE, echo = FALSE)
path |
character scalar, the R package directory |
dependencies |
Default |
echo |
Default |
For more details, see
Cargo docs
for cargo-metadata. See especially "JSON Format" to get a sense of what you
can expect to find in the returned list.
A list including the following elements:
packages
workspace_members
workspace_default_members
resolve
target_directory
version
workspace_root
metadata
## Not run: read_cargo_metadata() ## End(Not run)## Not run: read_cargo_metadata() ## End(Not run)
This function is deprecated because we now rely on a small Rust binary to generate wrappers, which is called during the package build process.
register_extendr(path = ".", quiet = FALSE, force = FALSE, compile = NA)register_extendr(path = ".", quiet = FALSE, force = FALSE, compile = NA)
path |
Path from which package root is looked up. |
quiet |
Logical indicating whether any progress messages should be generated or not. |
force |
Logical indicating whether to force regenerating
|
compile |
Logical indicating whether to recompile DLLs:
|
(Invisibly) Path to the file containing generated wrappers.
Compile and evaluate one or more Rust expressions. If the last
expression in the Rust code returns a value (i.e., does not end with
;), then this value is returned to R. The value returned does not need
to be of type Robj, as long as it can be cast into this type with
.into(). This conversion is done automatically, so you don't have to
worry about it in your code.
rust_eval(code, env = parent.frame(), ...)rust_eval(code, env = parent.frame(), ...)
code |
Input rust code. |
env |
The R environment in which the Rust code will be evaluated. |
... |
Other parameters handed off to |
The return value generated by the Rust code.
## Not run: # Rust code without return value, called only for its side effects rust_eval( code = 'rprintln!("hello from Rust!");' ) # Rust code with return value rust_eval( code = " let x = 5; let y = 7; let z = x * y; z // return to R; rust_eval() takes care of type conversion code " ) ## End(Not run)## Not run: # Rust code without return value, called only for its side effects rust_eval( code = 'rprintln!("hello from Rust!");' ) # Rust code with return value rust_eval( code = " let x = 5; let y = 7; let z = x * y; z // return to R; rust_eval() takes care of type conversion code " ) ## End(Not run)
Prints out a detailed report on the state of Rust infrastructure on the host machine.
rust_sitrep()rust_sitrep()
Nothing
rust_source() compiles and loads a single Rust file for use in R.
rust_function() compiles and loads a single Rust function for use in R.
extendr_options() is a helper function to make it easier to pass additional
options when sourcing Rust code. It also proivdes defaults for each option
and does additional type checking.
rust_source( file = NULL, code = NULL, env = parent.frame(), echo = FALSE, quiet = FALSE, opts = NULL, ... ) rust_function( code, extendr_fn_options = NULL, env = parent.frame(), echo = FALSE, quiet = FALSE, opts = NULL, ... ) extendr_options( cache_build = TRUE, dependencies = NULL, extendr_deps = NULL, features = NULL, generate_module_macro = TRUE, module_name = "rextendr", patch.crates_io = getOption("rextendr.patch.crates_io"), profile = c("dev", "release", "perf"), toolchain = getOption("rextendr.toolchain"), use_dev_extendr = FALSE, use_extendr_api = TRUE, use_rtools = TRUE ) ## S3 method for class 'extendr_opts' print(x, ...)rust_source( file = NULL, code = NULL, env = parent.frame(), echo = FALSE, quiet = FALSE, opts = NULL, ... ) rust_function( code, extendr_fn_options = NULL, env = parent.frame(), echo = FALSE, quiet = FALSE, opts = NULL, ... ) extendr_options( cache_build = TRUE, dependencies = NULL, extendr_deps = NULL, features = NULL, generate_module_macro = TRUE, module_name = "rextendr", patch.crates_io = getOption("rextendr.patch.crates_io"), profile = c("dev", "release", "perf"), toolchain = getOption("rextendr.toolchain"), use_dev_extendr = FALSE, use_extendr_api = TRUE, use_rtools = TRUE ) ## S3 method for class 'extendr_opts' print(x, ...)
file |
character scalar, input rust file to source. |
code |
character scalar, input rust code to be used instead of |
env |
environment, the R environment in which the wrapping functions
will be defined. Default is |
echo |
logical scalar, whether to print standard output and errors of
|
quiet |
logical scalar, whether to print |
opts |
|
... |
user supplied extendr options to be injected into the
|
extendr_fn_options |
A list of extendr function options that are
inserted into the |
cache_build |
logical scalar, whether builds should be cached between
calls to |
dependencies |
character vector, dependencies to be added to |
extendr_deps |
named list, versions of |
features |
character vector, |
generate_module_macro |
logical scalar, whether the Rust module
macro should be automatically generated from the code. Default is |
module_name |
character scalar, name of the module defined in the Rust source via
|
patch.crates_io |
character vector, patch statements for crates.io to
be added to |
profile |
character scalar, Rust profile. Can be either |
toolchain |
character scalar, Rust toolchain. The default, |
use_dev_extendr |
logical scalar, whether to use development version of
|
use_extendr_api |
logical scalar, whether |
use_rtools |
logical scalar, whether to append the path to Rtools to the
|
x |
an |
For rust_source() and rust_function(), the result from
dyn.load(), which is an object of class DLLInfo. See getLoadedDLLs()
for more details. For extendr_options(), an extendr_opts list.
## Not run: # creating a single rust function rust_function("fn add(a:f64, b:f64) -> f64 { a + b }") add(2.5, 4.7) # creating multiple rust functions at once code <- r"( #[extendr] fn hello() -> &'static str { "Hello, world!" } #[extendr] fn test( a: &str, b: i64) { rprintln!("Data sent to Rust: {}, {}", a, b); } )" rust_source(code = code) hello() test("a string", 42) # use case with an external dependency: a function that converts # markdown text to html, using the `pulldown_cmark` crate. code <- r"( use pulldown_cmark::{Parser, Options, html}; #[extendr] fn md_to_html(input: &str) -> String { let mut options = Options::empty(); options.insert(Options::ENABLE_TABLES); let parser = Parser::new_ext(input, options); let mut output = String::new(); html::push_html(&mut output, parser); output } )" rust_source( code = code, opts = extendr_options( dependencies = list(`pulldown-cmark` = "0.8") ) ) md_text <- "# The story of the fox The quick brown fox **jumps over** the lazy dog. The quick *brown fox* jumps over the lazy dog." md_to_html(md_text) # see default options extendr_options() ## End(Not run)## Not run: # creating a single rust function rust_function("fn add(a:f64, b:f64) -> f64 { a + b }") add(2.5, 4.7) # creating multiple rust functions at once code <- r"( #[extendr] fn hello() -> &'static str { "Hello, world!" } #[extendr] fn test( a: &str, b: i64) { rprintln!("Data sent to Rust: {}, {}", a, b); } )" rust_source(code = code) hello() test("a string", 42) # use case with an external dependency: a function that converts # markdown text to html, using the `pulldown_cmark` crate. code <- r"( use pulldown_cmark::{Parser, Options, html}; #[extendr] fn md_to_html(input: &str) -> String { let mut options = Options::empty(); options.insert(Options::ENABLE_TABLES); let parser = Parser::new_ext(input, options); let mut output = String::new(); html::push_html(&mut output, parser); output } )" rust_source( code = code, opts = extendr_options( dependencies = list(`pulldown-cmark` = "0.8") ) ) md_text <- "# The story of the fox The quick brown fox **jumps over** the lazy dog. The quick *brown fox* jumps over the lazy dog." md_to_html(md_text) # see default options extendr_options() ## End(Not run)
list() into toml-compatible format.to_toml() can be used to build Cargo.toml.
The cargo manifest can be represented in terms of
R objects, allowing limited validation and syntax verification.
This function converts manifests written using R objects into
toml representation, applying basic formatting,
which is ideal for generating cargo
manifests at runtime.
to_toml(..., .str_as_literal = TRUE, .format_int = "%d", .format_dbl = "%g")to_toml(..., .str_as_literal = TRUE, .format_int = "%d", .format_dbl = "%g")
... |
A list from which toml is constructed. Supports nesting and tidy evaluation. |
.str_as_literal |
Logical indicating whether to treat
strings as literal (single quotes no escapes) or
basic (escaping some sequences) ones. Default is |
.format_int, .format_dbl
|
Character scalar describing
number formatting. Compatible with |
A character vector, each element corresponds to one line of the resulting output.
# Produces [workspace] with no children to_toml(workspace = NULL) to_toml(patch.crates_io = list(`extendr-api` = list(git = "git-ref"))) # Single-element arrays are distinguished from scalars # using explicitly set `dim` to_toml(lib = list(`crate-type` = array("cdylib", 1)))# Produces [workspace] with no children to_toml(workspace = NULL) to_toml(patch.crates_io = list(`extendr-api` = list(git = "git-ref"))) # Single-element arrays are distinguished from scalars # using explicitly set `dim` to_toml(lib = list(`crate-type` = array("cdylib", 1)))
When a new version of extendr or rextendr is released, this function updates relevant scaffolding files to the new specification.
update_scaffold(path = ".", crate_name = NULL, lib_name = NULL, quiet = FALSE)update_scaffold(path = ".", crate_name = NULL, lib_name = NULL, quiet = FALSE)
path |
File path to the package for which to generate wrapper code. |
crate_name |
String that is used as the name of the Rust crate,
specifically |
lib_name |
String that is used as the name of the Rust library,
specifically |
quiet |
Logical indicating whether any progress messages should be generated or not. |
This function does not touch any build artifacts or files or folders generated when vendoring cargo. Cargo.lock and Cargo.toml are also left unchanged. Only the following files are re-written:
src/entrypoint.c
src/Makevars.in
src/Makevars.win.in
cleanup
cleanup.win
src/rust/document.rs
tools/msrv.R
tools/config.R
configure
configure.win
After updating these files, update_scaffold() will print a message that
explains what to do next to get your package up-to-date with the latest
versions of extendr and rextendr (provided quiet = FALSE, anyway). That
will typically include handling dependency resolution, updating Cargo.toml
and Cargo.lock, and vendoring crates for CRAN compliance. Usually, this
will be accompanied by a more detailed blog post explaining the update
process.
a logical scalar indicating whether scaffold updating was successful
Analogous to usethis::use_package() but for crate dependencies.
use_crate( crate, features = NULL, git = NULL, version = NULL, optional = FALSE, path = ".", echo = TRUE )use_crate( crate, features = NULL, git = NULL, version = NULL, optional = FALSE, path = ".", echo = TRUE )
crate |
character scalar, the name of the crate to add |
features |
character vector, a list of features to include from the crate |
git |
character scalar, the full URL of the remote Git repository |
version |
character scalar, the version of the crate to add |
optional |
boolean scalar, whether to mark the dependency as optional (FALSE by default) |
path |
character scalar, the package directory |
echo |
logical scalar, should cargo command and outputs be printed to console (default is TRUE) |
For more details regarding these and other options, see the
Cargo docs
for cargo-add.
NULL (invisibly)
## Not run: # add to [dependencies] use_crate("serde") # add to [dependencies] and [features] use_crate("serde", features = "derive") # add to [dependencies] using github repository as source use_crate("serde", git = "https://github.com/serde-rs/serde") # add to [dependencies] with specific version use_crate("serde", version = "1.0.1") # add to [dependencies] with optional compilation use_crate("serde", optional = TRUE) ## End(Not run)## Not run: # add to [dependencies] use_crate("serde") # add to [dependencies] and [features] use_crate("serde", features = "derive") # add to [dependencies] using github repository as source use_crate("serde", git = "https://github.com/serde-rs/serde") # add to [dependencies] with specific version use_crate("serde", version = "1.0.1") # add to [dependencies] with optional compilation use_crate("serde", optional = TRUE) ## End(Not run)
Create the scaffolding needed to add Rust extendr code to an R package.
use_extendr() adds a small Rust library with a single Rust function that
returns the string "Hello world!". It also adds wrapper code so this Rust
function can be called from R with hello_world().
use_extendr( path = ".", crate_name = NULL, lib_name = NULL, quiet = FALSE, overwrite = NULL, edition = c("2021", "2018") )use_extendr( path = ".", crate_name = NULL, lib_name = NULL, quiet = FALSE, overwrite = NULL, edition = c("2021", "2018") )
path |
File path to the package for which to generate wrapper code. |
crate_name |
String that is used as the name of the Rust crate,
specifically |
lib_name |
String that is used as the name of the Rust library,
specifically |
quiet |
Logical indicating whether any progress messages should be generated or not. |
overwrite |
Logical scalar or |
edition |
String indicating which Rust edition is used; Default |
R/extendr-wrappers.R: auto-generated R wrappers. Do not edit by hand.
src/entrypoint.c: C entry point forwarding R's routine registration to
the Rust library.
src/Makevars.in / src/Makevars.win.in: Makefile templates compiled and
employed at package build time.
src/<pkg>-win.def: Windows DLL export definitions.
src/.gitignore: Ignores compiled artifacts, Cargo directories, and
generated Makevars files.
src/rust/Cargo.toml: Rust package manifest with crate name, edition,
extendr-api dependency, and release profile settings.
src/rust/src/lib.rs: Main Rust library with an example hello_world()
function and the extendr_module! macro.
src/rust/document.rs: Rust binary that writes R/extendr-wrappers.R
by introspecting exported function metadata at build time.
tools/msrv.R: Verifies the installed Rust toolchain meets the MSRV in
DESCRIPTION.
tools/config.R: Reads tools/msrv.R, checks DEBUG/NOT_CRAN env
vars, and writes the final Makevars file from the .in template.
configure / configure.win: Shell scripts run before compilation that
invoke tools/config.R via Rscript.
cleanup / cleanup.win: Shell scripts that remove src/Makevars on
package uninstall.
A logical value (invisible) indicating whether any package files were generated or not.
Add the version of extendr being used by an R package to its README.
use_extendr_badge(path = ".")use_extendr_badge(path = ".")
path |
File path to the package for which to generate wrapper code. |
Requires usethis to be available.
## Not run: use_extendr_badge() ## End(Not run)## Not run: use_extendr_badge() ## End(Not run)
use_msrv() sets the minimum supported rust version for your R package.
use_msrv(version, path = ".", overwrite = FALSE)use_msrv(version, path = ".", overwrite = FALSE)
version |
character scalar, the minimum supported Rust version. |
path |
character scalar, path to folder containing DESCRIPTION file. |
overwrite |
default |
The minimum supported rust version (MSRV) is determined by the
SystemRequirements field in a package's DESCRIPTION file. For example, to
set the MSRV to 1.67.0, the SystemRequirements must have
rustc >= 1.67.0.
By default, there is no MSRV set. However, some crates have features that depend on a minimum version of Rust. As of this writing the version of Rust on CRAN's Fedora machine's is 1.69. If you require a version of Rust that is greater than that, you must set it in your DESCRIPTION file.
It is also important to note that if CRAN's machines do not meet the specified MSRV, they will not be able to build a binary of your package. As a consequence, if users try to install the package they will be required to have Rust installed as well.
To determine the MSRV of your R package, we recommend installing the
cargo-msrv cli. You can do so by running cargo install cargo-msrv. To
determine your MSRV, set your working directory to src/rust then run
cargo msrv. Note that this may take a while.
For more details, please see cargo-msrv.
version
## Not run: use_msrv("1.67.1") ## End(Not run)## Not run: use_msrv("1.67.1") ## End(Not run)
This creates a .vscode folder (if needed) and populates it with a
settings.json template. If already exists, it will be updated to include
the rust-analyzer.linkedProjects setting.
use_vscode(quiet = FALSE, overwrite = NULL) use_positron(quiet = FALSE, overwrite = NULL)use_vscode(quiet = FALSE, overwrite = NULL) use_positron(quiet = FALSE, overwrite = NULL)
quiet |
If |
overwrite |
If |
Rust-Analyzer VSCode extension looks for a Cargo.toml file in the
workspace root by default. This function creates a .vscode folder and
populates it with a settings.json file that sets the workspace root to
the src directory of the package. This allows you to open the package
directory in VSCode and have the Rust-Analyzer extension work correctly.
TRUE (invisibly) if the settings file was created or updated.
vendor_crates() is used to package the dependencies as required by CRAN.
It executes cargo vendor on your behalf creating a vendor/ directory and a
compressed vendor.tar.xz which will be shipped with package itself.
If you have modified your dependencies, you will need need to repackage
the vendored dependencies using vendor_crates().
vendor_pkgs() was renamed to vendor_crates().
vendor_crates(path = ".", quiet = FALSE, overwrite = NULL, clean = FALSE) vendor_pkgs(path = ".", quiet = FALSE, overwrite = NULL, clean = FALSE)vendor_crates(path = ".", quiet = FALSE, overwrite = NULL, clean = FALSE) vendor_pkgs(path = ".", quiet = FALSE, overwrite = NULL, clean = FALSE)
path |
File path to the package for which to generate wrapper code. |
quiet |
Logical indicating whether any progress messages should be generated or not. |
overwrite |
Logical scalar or |
clean |
|
vendor_crates() returns a data.frame with two columns crate and version
## Not run: vendor_crates() ## End(Not run)## Not run: vendor_crates() ## End(Not run)
write_license_note(path = ".", quiet = FALSE, force = TRUE)write_license_note(path = ".", quiet = FALSE, force = TRUE)
path |
character scalar, the R package directory |
quiet |
logical scalar, whether to signal successful writing of
LICENSE.note (default is |
force |
logical scalar, whether to regenerate LICENSE.note if
LICENSE.note already exists (default is |
LICENSE.note generated by this function contains information about all recursive dependencies in Rust crate.
The authors field in Cargo metadata is deprecated upstream
(see https://github.com/rust-lang/cargo/issues/16458). This function
relies on that field and may produce incomplete or missing author
information as crates stop populating or removes the field entirely.
text printed to LICENSE.note (invisibly).
## Not run: write_license_note() ## End(Not run)## Not run: write_license_note() ## End(Not run)