我正在学习如何使用rextendr
R包和extendr
Rust板条箱连接Rust和R(除了学习Rust本身)。当我在R函数的输入中使用:
时,我遇到了错误Expected a vector type.
。
下面是一个简单函数的例子,如果输入向量中的所有值都是正的,则返回TRUE
,否则返回FALSE
。当我在输入中使用c()
时,它似乎工作得很好,但当我使用:
时就不行了。
library(rextendr)
# create a Rust function that checks if all values in the input are
# greater than 0
rust_function(
"fn all_positive(input: &[f64]) -> bool {
let mut out = true;
for i in input.iter() {
if *i <= 0.0 {
out = false;
break
}
}
out
}"
)
#> ℹ build directory: '/tmp/RtmpeoMSEH/file7f971642e99e'
#> ✔ Writing '/tmp/RtmpeoMSEH/file7f971642e99e/target/extendr_wrappers.R'.
# call it from R
all_positive(c(1, 2, 3))
#> [1] TRUE
all_positive(c(0, 1, 2))
#> [1] FALSE
all_positive(1:3)
#> Error in all_positive(1:3): Expected a vector type.
为什么会这样?我该如何解决?
- PS:因为我是从Rust开始的,所以请随意提及Rust代码中的任何其他错误/非惯用的东西。*
1条答案
按热度按时间mzsu5hc01#
需要接受
integer
或numeric
类型的Rust函数1:3
创建一个integer
向量,c(1,2,3)
创建一个numeric
向量。示例中的函数需要f64
,即numeric
输入,因此不喜欢1:3
。您可以编写一个同时接受这两个向量的函数,例如:Rbool
,而不是Rustbool
*,这样如果你用一个非数值类型调用函数,例如字符向量,它可以返回 *NA_logical_
。更多详情
这是TL;医生,这里有更多的信息。这里有三个因素在起作用:
:
运算符意味着R创建了一个integer
向量,而不是numeric
向量。f64
值的数组切片。更一般地说,Rust要求函数参数是显式类型,或者是具有共享特性的generic。extendr
does not support generics,根据设计。integer
与numeric
::
带来了多大的不同正如Dirk Eddelbuettel在评论中指出的,按照惯例,
:
运算符意味着一个整数向量(甚至在ALTREP
之前):我们可以使用
lobstr
包查看底层的C表示:如R Internals中所述,
INTSXP
是Cint
值的块,REALSXP
是Cdouble
值的块。将
INTSXP
和REALSXP
翻译成Rust世界我们可以使用
extendr-api
、extendr-engine
和extendr-macros
板条箱(v.0.4.0
)直接从Rust看到extendr
如何MapR类型,使用R!
macro:这个简单的调试输出确认
1:3
对象被输出为整数集合(i32
),而c(1,2,3)
被输出为浮点数,即Rust世界中的f64
。泛型
现在我们知道我们正在处理不同的类型,我的诱惑是这样做:
但是--虽然这将在Rust中编译,但它不会编译为导出的
extendr
函数。创建采用
integer
和numeric
类型的Rust函数一个
extendr
Github的问题是缺少泛型支持suggests,创建一个Rust函数,该函数采用Robj
而不是Rust类型,并在Rust中解决这个问题。在这种情况下,我们可以使用Robj::as_integer_vector()
和Robj::as_real_vector
方法。这些函数的文档似乎还在编写中,但是我们可以从源代码中看到它们返回的是
Option<T>
类型,即Some(<T>)
或None
。我们可以使用match
构造来尝试将从R接收到的值转换为整数和浮点向量,并且只在得到Some()
类型时才执行我们想要执行的操作。顺便说一句,Rust最棒的地方之一是它和R非常相似,都可以使用迭代器而不是循环,我想我们可以在代码的主要部分做到这一点,我们可以使用非常类似于
iter.all()
文档的代码:如果你不想有两个比较,你也可以type cast
i32
到f64
,尽管我们可以在match
语句中链接.iter()
,我没有打扰。这将允许我们为
1:3
和c(1,2,3)
调用在开始时定义的函数。