将R对象传递给Rust程序需要哪些步骤?

Zel*_*ny7 9 r rust

R和Rust都可以与C代码接口,所以我认为这很有可能.但是,我对如何继续有点不清楚.

我已阅读这些部分寻找答案:

  1. R-extensions系统和外语接口
  2. Rust外部函数接口指南

但是,虽然我精通,R但我不是一个系统程序员,并且对构建链看起来像这样的努力感到困惑.

使用Rinternals.h将是理想的,但我也会满足于更简单的.C界面.

Sam*_*yer 8

我一直在努力争取这个问题,但是一旦你知道我实际上并不那么困难.

首先按照以下说明创建一个Rust库:rust-inside-other-languages.这是一个示例Rust库:

//src/lib.rs

#[no_mangle]
pub fn kelvin_to_fahrenheit(n: f64) -> f64 {
    n * 9.0/5.0 - 459.67
}
Run Code Online (Sandbox Code Playgroud)

如果您按照其他语言生锈的说明进行操作,那么您应该能够生成*.so(*.dll或者.dylib,取决于您的系统).让我们假设这个编译过的文件被调用了libtempr.so.

现在创建一个C++文件,它将把你需要的函数传递给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);
}
Run Code Online (Sandbox Code Playgroud)

现在,在启动R之前,请确保环境变量LD_LIBRARY_PATH包含libtempr.so存储先前生成的共享对象()的目录.在shell中执行:

$ export LD_LIBRARY_PATH=/home/sam/path/to/shared/object:$LD_LIBRARY_PATH
$ rstudio # I highly recommend using Rstudio for your R coding
Run Code Online (Sandbox Code Playgroud)

最后在Rstudo中,写下这个文件:

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)
Run Code Online (Sandbox Code Playgroud)
  • 请注意,Sys.setenv-L选项指向包含Rust共享对象的目录.
  • 另外要注意,-loption是没有lib前缀的共享对象的名称,也没有.so(或系统中的任何内容)后缀.
  • Sys.setenv在R中使用设置LD_LIBRARY_PATH变量不工作.在开始R之前导出变量.
  • verbose选项是有那么你可以看到什么Rcpp呢编译C++文件.请注意PKG_LIBS上面的选项如何用于编译C++文件.
  • rebuild选项是有强制每次运行此行的R代码里面的时间重建C++文件.

如果你做得很好,那么在交互式控制台中运行上面的R文件,80.33当你到达最后一行时它应该输出.

如果有任何不清楚的地方,请在评论中提问,我会尽力改善我的答案.

希望它有帮助:)


最后要注意,基函数dyn.load.C可以被用作一个替代方法.但这需要编写比这种方法更多的样板包装代码.


swi*_*ard 2

如果 R 可以与 C 代码交互,那么从公开 C 风格函数的 Rust 代码编译共享库完全没有问题。

然后您就可以轻松使用您的库,因为它是用 C 或 C++ 编写的。当然,您将无法直接从 R 使用 Rust 对象和库,您必须创建适当的 C 接口来转换它们的函数。

以下是我如何为 SBCL 做到这一点,我想这对于 R 来说非常相似:

在 Rust 一侧

一些代码:

% 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,
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我正在制作共享库:

% 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
Run Code Online (Sandbox Code Playgroud)

接下来,在SBCL方面

现在我只需加载我的共享库,然后我就可以访问我的rust_code_string_to_int函数:

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
Run Code Online (Sandbox Code Playgroud)