#api #user #user-friendly #extendr #extension #language #safe

aorist_extendr-api

为 aorist 项目 Fork extendr-api。对 R 编程语言的安全且用户友好的绑定。

1 个不稳定版本

0.0.1 2021 年 7 月 23 日

#4#extendr

42 每月下载
用于 5 个包 (4 个直接使用)

MIT 许可证

265KB
5.5K SLoC

extendr-api

安全且用户友好的 R 扩展接口。

  • 构建 R 扩展到 Rust。
  • 将 R 包转换为 Rust 包。

该库旨在为 Rust 或任何编译语言的新手用户提供一个熟悉的接口。

有关此包的大部分内容,请参阅 [Robj]。 [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 中,越界会导致错误(恐慌),而 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);
}

许可证:MIT

依赖关系

~5.5MB
~135K SLoC