提问人:DrStrangeLove 提问时间:11/15/2023 最后编辑:DrStrangeLove 更新时间:11/15/2023 访问量:61
为具有通用 Rust 类型的 Rust 函数实现 Python 接口
Implementing a Python interface for a Rust function with generic Rust type
问:
这个函数在 Rust 上完美运行:
fn jaccard_similarity<T>(s1: Vec<T>, s2: Vec<T>) -> f32
where
T: Hash + Eq + Clone,
{
let s1 = vec_to_set(&s1);
let s2 = vec_to_set(&s2);
let i = s1.intersection(&s2).count() as f32;
let u = s1.union(&s2).count() as f32;
return i / u;
}
fn vec_to_set<T>(vec: &Vec<T>) -> HashSet<T>
where
T: Hash + Eq + Clone,{
HashSet::from_iter(vec.iter().cloned())
}
在以下测试用例上:
#[test]
fn test_jaccard_similarity() {
let left = vec!["kitten", "sitting", "saturday", "sunday"];
let right = vec!["kitten", "sitting", "saturday", "sunday"];
assert_eq!(jaccard_similarity(left, right), 1.0);
let left = vec![1,2,3,4];
let right = vec![1,2,3,4];
assert_eq!(jaccard_similarity(left, right), 1.0);
let left = vec![1,2,3,4];
let right = vec![2,2,3,4];
assert_eq!(jaccard_similarity(left, right), 0.75);
}
但是,一旦我将其包装为 pyo3 crate [版本:0.13.2] 的 #[pyfunction](并且我还更新了我的 lib.rs 和 mod.rs 文件)。对于上下文,我正在使用 Maturin 库。
#[pyfunction]
fn jaccard_similarity<T>(s1: Vec<T>, s2: Vec<T>) -> f32
where
T: Hash + Eq + Clone,
{
let s1 = vec_to_set(&s1);
let s2 = vec_to_set(&s2);
let i = s1.intersection(&s2).count() as f32;
let u = s1.union(&s2).count() as f32;
return i / u;
}
我收到以下错误:
--> src\distance_functions\jaccard_similarity.rs:6:4
|
6 | fn jaccard_similarity<T>(s1: Vec<T>, s2: Vec<T>) -> f32
| ^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `jaccard_similarity`
|
= note: cannot satisfy `_: Hash`
note: required by a bound in `jaccard_similarity`
--> src\distance_functions\jaccard_similarity.rs:8:8
|
6 | fn jaccard_similarity<T>(s1: Vec<T>, s2: Vec<T>) -> f32
| ------------------ required by a bound in this function
7 | where
8 | T: Hash + Eq + Clone,
| ^^^^ required by this bound in `jaccard_similarity`
help: consider specifying the generic argument
|
6 | fn jaccard_similarity::<T><T>(s1: Vec<T>, s2: Vec<T>) -> f32
| +++++
泛型参数已在函数上声明。我无法理解编译器要求我做什么。
当 Rust 代码被包装在 Python 接口中时,在 Rust 中工作的内容也应该如此。
编辑:我将pyo3版本更新为0.20.0,现在我收到更有意义的错误消息:
error: Python functions cannot have generic type parameters
--> src\distance_functions\jaccard_similarity.rs:6:23
|
6 | fn jaccard_similarity<T>(s1: Vec<T>, s2: Vec<T>) -> f32
有没有办法对 Python 函数使用泛型类型参数?
答:
当 Rust 代码被包装在 Python 接口中时,在 Rust 中工作的内容也应该如此。
不。Python 不是静态类型的,python 接口不支持泛型,因此 pyo3 无法创建桥接函数和 Rust 实现的绑定。
事实上,这与 Rust 本身的行为相匹配:它本身实际上不会生成任何代码。相反,编译器将查看调用站点,并为调用函数的每个实例生成一个实例,这些实例是最终进入二进制文件的代码。实例化步骤是 pyo3 无法实现的步骤,因此它无法工作。jaccard_similarity
T
我还想说这段代码从根本上没有用,python 等价物是用 C 实现的 5 次调用(创建两个集合,将它们相交,将它们联合起来并除以)。将数据复制到向量,然后设置这些向量的开销可能与让 python 做这件事一样大。尤其是 Rust 的默认哈希函数。
为了获得任何实际收益的机会,我认为您可能需要完全避免这两个 vec(从 PyList 动态执行转换)并避免重新化其中一个集合(可能是更大的集合)。
评论