17个不稳定版本 (5个破坏性版本)
0.7.0 | 2024年6月29日 |
---|---|
0.6.0 | 2023年10月6日 |
0.4.0 | 2023年2月26日 |
0.3.1 | 2022年5月30日 |
0.1.0 | 2020年7月5日 |
#50 in Rust模式
3,108 每月下载量
用于 4 crates
630KB
13K SLoC
extendr-api
一个安全且用户友好的R扩展接口。
- 构建R扩展到R。
- 将R包转换为Rust crate。
这个库旨在为Rust或任何编译语言的新手用户提供一个熟悉的接口。
安装
只需将此行添加到您的 [dependencies]
部分 Cargo.toml
。然后您将能够从Rust中调用R代码。
[dependencies]
extendr-api = "0.6"
概述
参阅 [Robj],了解此crate的大部分内容。[Robj] 为R对象类型提供了一个安全的包装器。
使用属性和宏来导出到R。
use extendr_api::prelude::*;
// Export a function or impl to R.
#[extendr]
fn fred(a: i32) -> i32 {
a + 1
}
// define exports using extendr_module
extendr_module! {
mod mymodule;
fn fred;
}
在R中
result <- fred(1)
[Robj] 是R对象的包装器。r!() 和 R!() 宏允许您分别使用Rust和R语法构建R对象。
use extendr_api::prelude::*;
test! {
// An R object with a single string "hello"
let character = r!("hello");
let character = r!(["hello", "goodbye"]);
// An R integer object with a single number 1L.
// Note that in Rust, 1 is an integer and 1.0 is a real.
let integer = r!(1);
// An R real object with a single number 1.
// Note that in R, 1 is a real and 1L is an integer.
let real = r!(1.0);
// An R real vector.
let real_vector = r!([1.0, 2.0]);
let real_vector = &[1.0, 2.0].iter().collect_robj();
let real_vector = r!(vec![1.0, 2.0]);
// An R function object.
let function = R!("function(x, y) { x + y }")?;
// A named list using the list! macro.
let list = list!(a = 1, b = 2);
// An unnamed list (of R objects) using the List wrapper.
let list = r!(List::from_values(vec![1, 2, 3]));
let list = r!(List::from_values(vec!["a", "b", "c"]));
let list = r!(List::from_values(&[r!("a"), r!(1), r!(2.0)]));
// A symbol
let sym = sym!(wombat);
// A R vector using collect_robj()
let vector = (0..3).map(|x| x * 10).collect_robj();
}
在Rust中,我们更喜欢使用迭代器而不是循环。
use extendr_api::prelude::*;
test! {
// 1 ..= 100 is the same as 1:100
let res = r!(1 ..= 100);
assert_eq!(res, R!("1:100")?);
// Rust arrays are zero-indexed so it is more common to use 0 .. 100.
let res = r!(0 .. 100);
assert_eq!(res.len(), 100);
// Using map is a super fast way to generate vectors.
let iter = (0..3).map(|i| format!("fred{}", i));
let character = iter.collect_robj();
assert_eq!(character, r!(["fred0", "fred1", "fred2"]));
}
要索引一个向量,首先将其转换为切片,然后记得使用基于0的索引。在Rust中,越界将会导致错误(panic),而C++可能会崩溃。
use extendr_api::prelude::*;
test! {
let vals = r!([1.0, 2.0]);
let slice = vals.as_real_slice().ok_or("expected slice")?;
let one = slice[0];
let two = slice[1];
// let error = slice[2];
assert_eq!(one, 1.0);
assert_eq!(two, 2.0);
}
这些方法更慢,但更通用
use extendr_api::prelude::*;
test! {
let vals = r!([1.0, 2.0, 3.0]);
// one-based indexing [[i]], returns an object.
assert_eq!(vals.index(1)?, r!(1.0));
// one-based slicing [x], returns an object.
assert_eq!(vals.slice(1..=2)?, r!([1.0, 2.0]));
// $ operator, returns an object
let list = list!(a = 1.0, b = "xyz");
assert_eq!(list.dollar("a")?, r!(1.0));
}
[R!] 宏允许您在Rust中嵌入R代码,并使用 {{ }} 对对Rust表达式进行包装。
[Rraw!] 宏不会展开 {{ }} 对对。
use extendr_api::prelude::*;
test! {
// The text "1 + 1" is parsed as R source code.
// The result is 1.0 + 1.0 in Rust.
assert_eq!(R!("1 + 1")?, r!(2.0));
let a = 1.0;
assert_eq!(R!("1 + {{a}}")?, r!(2.0));
assert_eq!(R!(r"
x <- {{ a }}
x + 1
")?, r!(2.0));
assert_eq!(R!(r#"
x <- "hello"
x
"#)?, r!("hello"));
// Use the R meaning of {{ }} and do not expand.
assert_eq!(Rraw!(r"
x <- {{ 1 }}
x + 1
")?, r!(2.0));
}
[r!] 宏将Rust对象转换为R对象,并接受参数。
use extendr_api::prelude::*;
test! {
// The text "1.0+1.0" is parsed as Rust source code.
let one = 1.0;
assert_eq!(r!(one+1.0), r!(2.0));
}
您可以使用 [call!] 宏调用R函数和原语。
use extendr_api::prelude::*;
test! {
// As one R! macro call
let confint1 = R!("confint(lm(weight ~ group - 1, PlantGrowth))")?;
// As many parameterized calls.
let formula = call!("~", sym!(weight), lang!("-", sym!(group), 1))?;
let plant_growth = global!(PlantGrowth)?;
let model = call!("lm", formula, plant_growth)?;
let confint2 = call!("confint", model)?;
assert_eq!(confint1.as_real_vector(), confint2.as_real_vector());
}
Rust有一个“所有者”和“借用”对象的概念。
拥有的对象,例如 [Vec] 和 [String],在对象生命周期结束时释放内存。
借用对象,如 &[i32] 和 &str,只是指向另一个对象内存的指针,它们的寿命不能超过它们所引用的对象。
借用对象比拥有的对象快得多,并且使用更少的内存,但仅用于临时访问。
例如,当我们从 R 向量中取一个切片时,我们需要原始 R 对象保持存活,否则数据将损坏。
use extendr_api::prelude::*;
test! {
// robj is an "Owned" object that controls the memory allocated.
let robj = r!([1, 2, 3]);
// Here slice is a "borrowed" reference to the bytes in robj.
// and cannot live longer than robj.
let slice = robj.as_integer_slice().ok_or("expected slice")?;
assert_eq!(slice.len(), 3);
}
功能门
extendr-api 在这些功能门后面有一些可选功能
ndarray
:提供 R 矩阵和ndarray
之间的转换。num-complex
:提供 R 复数和num-complex
之间的转换。serde
:提供serde
支持。graphics
:提供控制或实现图形设备的功能。either
:提供Either<L, R>
从either
的类型转换特质的实现,如果L
和R
都实现了这些特质。faer
:提供 R 矩阵和faer
之间的转换。
extendr-api 对 Result<T,E>
到 Robj
有不同的编码(转换)。在下面的 x_ok
代表从 rust 通过 T::into_robj()
或类似方式返回的 R 侧的 R 变量。同样,x_err
通过 E::into_robj()
或类似方式从 rust 返回到 R 侧。extendr-api
result_list'
Ok(T)
被编码为list(ok = x_ok, err = NULL)
,而Err
被编码为list(ok = NULL, err = e_err)
result_condition'
Ok(T)
被编码为x_ok
,而Err(E)
被编码为condition(msg="extendr_error", value = x_err, class=c("extendr_error", "error", "condition"))
- 以上结果功能门的倍数。只有一个结果功能门将生效,当前优先级为[
result_list
,result_condition
,... ]。
最后,R的API中有一部分被视为非API,因此建议CRAN上的R包不提供这些功能。如果您想访问它们,可以使用non-api
来暴露它们。但是,需要设置bindgen
,并具体遵循以下libR-sys
的设置说明。
许可证
MIT
依赖关系
~4–7MB
~167K SLoC