Both R and Rust can interface with C code, so I think it is very possible. I am a bit unclear about how to proceed, however.
I have read these sections looking for answers:
But while I am well-versed in R
I am not a systems programmer and confused by what the build-chain looks like for such an endeavor.
Using Rinternals.h
would be ideal, but I would settle for the simpler .C
interface as well.
I have been struggling on this for a while as well, but once you know how I'ts actually not that difficult.
First create a Rust library following these instructions: rust-inside-other-languages. Here is an example Rust library:
//src/lib.rs
#[no_mangle]
pub fn kelvin_to_fahrenheit(n: f64) -> f64 {
n * 9.0/5.0 - 459.67
}
If you follow the instructions in rust-inside-other-languages, then you should be able to generate a *.so
(or *.dll
or .dylib
, depending on your system). Let's presume this compiled file is called libtempr.so
.
Now create a C++ file which will pass the functions you need to R:
//embed.cpp
extern "C" {
double kelvin_to_fahrenheit(double);
}
// [[Rcpp::export]]
double cpp_kelvin_to_fahrenheit(double k) {
double f = kelvin_to_fahrenheit(k);
return(f);
}
Now before starting R, make sure the environment variable LD_LIBRARY_PATH
contains the directory where the shared object generated previously (libtempr.so
) is stored. In the shell do:
$ export LD_LIBRARY_PATH=/home/sam/path/to/shared/object:$LD_LIBRARY_PATH
$ rstudio # I highly recommend using Rstudio for your R coding
Finally in Rstudo, write this file:
library(Rcpp)
Sys.setenv("PKG_LIBS"="-L/home/sam/path/to/shared/object -ltempr")
sourceCpp("/home/sam/path/to/embed.cpp", verbose = T, rebuild = T)
cpp_kelvin_to_fahrenheit(300)
Sys.setenv
the -L
option points to the directory containing your Rust shared object.-l
option is the name of your shared object without the lib
prefix and without the .so
(or whatever it is on your system) postfix.Sys.setenv
in R to set the LD_LIBRARY_PATH
variable DOES NOT WORK. Export the variable before starting R.verbose
option is there so that you can see what Rcpp
does to compile your C++ file. Notice how the options in PKG_LIBS
above are used for compiling your C++ file.rebuild
options is there to force a rebuild of the C++ file every time you run this line of R code.If you did everything well, then run the R file above in the interactive console and it should output 80.33
when you reach the last line.
If anything is not clear, ask in the comments, and I'll try to improve my answer.
Hope it helped :)
Final note, the base functions dyn.load
and .C
can be used as an alternative approach. But this requires writing a lot more boilerplate wrapper code than this approach.
If R can interface with C code, so it is no problem at all to compile shared library from Rust code which exposes C-style functions.
Then you can easily use your library as it was written in C or C++. Of course, you will not able to use Rust object and libraries directly from R, you will have to make appropriate C interface for converting their functions.
Here is how can I do that for SBCL, and I suppose it would be very similar for R:
Some code:
% cat experiment.rs
extern crate libc;
use libc::{c_int, c_char};
use std::{ffi, str};
#[no_mangle]
pub extern fn rust_code_string_to_int(s: *const c_char, r: *mut c_int) -> c_int {
let string = String::from_utf8_lossy(unsafe { ffi::CStr::from_ptr(s).to_bytes() });
match <isize as str::FromStr>::from_str(&*string) {
Ok(value) => { unsafe { *r = value as c_int }; 0 },
Err(_) => -1,
}
}
Then I'm making shared lib:
% rustc --crate-type dylib experiment.rs
% nm -a libexperiment.dylib | grep rust_code_string_to_int
0000000000001630 t __ZN23rust_code_string_to_int10__rust_abiE
00000000000015e0 T _rust_code_string_to_int
Now I'm just loading my shared lib and then I have access to my rust_code_string_to_int
function:
RUST> (sb-alien:load-shared-object "libexperiment.dylib")
#P"libexperiment.dylib"
RUST> (sb-alien:with-alien ((result sb-alien:int 0))
(values (sb-alien:alien-funcall (sb-alien:extern-alien "rust_code_string_to_int"
(sb-alien:function sb-alien:int
(sb-alien:c-string :external-format :utf-8)
(sb-alien:* sb-alien:int)))
(sb-alien:make-alien-string "42")
(sb-alien:addr result))
result))
0
42
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With