Title: | Call Rust Code from R using the 'extendr' Crate |
---|---|
Description: | Provides functions to compile and load Rust code from R, similar to how 'Rcpp' or 'cpp11' allow easy interfacing with C++ code. Also provides helper functions to create R packages that use Rust code. Under the hood, the Rust crate 'extendr' is used to do all the heavy lifting. |
Authors: | Claus O. Wilke [aut] , Andy Thomason [aut], Mossa M. Reimert [aut], Ilia Kosenkov [aut, cre] , Malcolm Barrett [aut] , Josiah Parry [ctb] |
Maintainer: | Ilia Kosenkov <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.3.1.9001 |
Built: | 2024-12-16 22:21:08 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_pkgs()
.
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."
The function rextendr::document()
updates the package documentation for an
R package that uses extendr
code, taking into account any changes that were
made in the Rust code. It is a wrapper for devtools::document()
, and it
executes extendr
-specific routines before calling devtools::document()
.
Specifically, it ensures that Rust code is recompiled (when necessary) and that
up-to-date R wrappers are generated before regenerating the package documentation.
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 generates wrapper code corresponding to the extendr module
for an R package. This is useful in package development, where we generally
want appropriate R code wrapping the Rust functions implemented via extendr.
In most development settings, you will not want to call this function directly,
but instead call rextendr::document()
.
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:
|
The function register_extendr()
compiles the package Rust code if
required, and then the wrapper code is retrieved from the compiled
Rust code and saved into R/extendr-wrappers.R
. Afterwards, you will have
to re-document and then re-install the package for the wrapper functions to
take effect.
(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.
rust_source( file, code = NULL, module_name = "rextendr", dependencies = NULL, patch.crates_io = getOption("rextendr.patch.crates_io"), profile = c("dev", "release", "perf"), toolchain = getOption("rextendr.toolchain"), extendr_deps = NULL, features = NULL, env = parent.frame(), use_extendr_api = TRUE, generate_module_macro = TRUE, cache_build = TRUE, quiet = FALSE, use_rtools = TRUE, use_dev_extendr = FALSE ) rust_function( code, extendr_fn_options = NULL, env = parent.frame(), quiet = FALSE, use_dev_extendr = FALSE, ... )
rust_source( file, code = NULL, module_name = "rextendr", dependencies = NULL, patch.crates_io = getOption("rextendr.patch.crates_io"), profile = c("dev", "release", "perf"), toolchain = getOption("rextendr.toolchain"), extendr_deps = NULL, features = NULL, env = parent.frame(), use_extendr_api = TRUE, generate_module_macro = TRUE, cache_build = TRUE, quiet = FALSE, use_rtools = TRUE, use_dev_extendr = FALSE ) rust_function( code, extendr_fn_options = NULL, env = parent.frame(), quiet = FALSE, use_dev_extendr = FALSE, ... )
file |
Input rust file to source. |
code |
Input rust code, to be used instead of |
module_name |
Name of the module defined in the Rust source via
|
dependencies |
Character vector of dependencies lines to be added to the
|
patch.crates_io |
Character vector of patch statements for crates.io to
be added to the |
profile |
Rust profile. Can be either |
toolchain |
Rust toolchain. The default, |
extendr_deps |
Versions of |
features |
A vector of |
env |
The R environment in which the wrapping functions will be defined. |
use_extendr_api |
Logical indicating whether
|
generate_module_macro |
Logical indicating whether the Rust module
macro should be automatically generated from the code. Default is |
cache_build |
Logical indicating whether builds should be cached between
calls to |
quiet |
Logical indicating whether compile output should be generated or not. |
use_rtools |
Logical indicating whether to append the path to Rtools
to the |
use_dev_extendr |
Logical indicating whether to use development version of
|
extendr_fn_options |
A list of extendr function options that are inserted into
|
... |
Other parameters handed off to |
The result from dyn.load()
, which is an object of class DLLInfo
.
See getLoadedDLLs()
for more details.
## 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, 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) ## 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, 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) ## 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)))
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.
If |
lib_name |
String that is used as the name of the Rust library.
If |
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 |
A logical value (invisible) indicating whether any package files were generated or not.
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)
vendor_pkgs()
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
vendor_pkgs(path = ".", quiet = FALSE, overwrite = NULL)
vendor_pkgs(path = ".", quiet = FALSE, overwrite = NULL)
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 |
vendor_pkgs()
returns a data.frame with two columns crate
and version
## Not run: vendor_pkgs() ## End(Not run)
## Not run: vendor_pkgs() ## End(Not run)
LICENSE.note generated by this function contains information about all recursive dependencies in Rust crate.
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 |
text printed to LICENSE.note (invisibly).
## Not run: write_license_note() ## End(Not run)
## Not run: write_license_note() ## End(Not run)