我正在学习如何在Python中嵌入Rust函数,如果我的输入是int,但不是列表,那么一切都可以正常工作.

如果我的lib.rs文件是:

#[no_mangle]
pub extern fn my_func(x: i32, y: i32) -> i32 {
    return x + y;
}

我可以这样使用:

In [1]: from ctypes import cdll

In [2]: lib = cdll.LoadLibrary("/home/user/RustStuff/embed/target/release/libembed.so")

In [3]: lib.my_func(5,6)
Out[3]: 11

但是,如果我将我的lib.rs更改为:

#[no_mangle]
pub extern fn my_func(my_vec: Vec<i32>) -> i32 {
    let mut my_sum = 0;
    for i in my_vec {
        my_sum += i;
    }
    return my_sum;
}

我不能再在Python中使用它(这很好):

In [1]: from ctypes import cdll

In [2]: lib = cdll.LoadLibrary("/home/user/RustStuff/embed/target/release/libembed.so")

In [3]: lib.my_func([2,3,4])
---------------------------------------------------------------------------
ArgumentError                             Traceback (most recent call last)
<ipython-input-3-454ffc5ba9dd> in <module>()
----> 1 lib.my_func([2,3,4])

ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1

原因是,我认为这可能会奏效,因为Python的list和Rust的Vec都是dynamic arrays,但显然我在这里遗漏了一些东西...

为什么我的try 不起作用?我该怎么做才能修好它?

推荐答案

不要这样做:

#[no_mangle]
pub extern fn my_func(my_vec: Vec<i32>) -> i32 { ... }

基本上,您希望在extern函数中接受或返回一个任意的Rust对象,只有那些是Repr.相反,你应该接受一些可以用C来表示的东西.作为6502 says,对于这种特殊情况,最好的办法是接受一个指针和一个长度.

Rust的Vec在概念上是一个指向数据的指针,一个计数,and a capacity.您可以通过添加或删除对象来修改Vec,这可能会导致重新分配.这是非常糟糕的,因为Python和Rust很可能使用不同的分配器,它们彼此不兼容.错误就在这边!你真的想要slice.

相反,在Rust 的一面做类似的事情:

extern crate libc;

use libc::{size_t,int32_t};
use std::slice;

#[no_mangle]
pub extern fn my_func(data: *const int32_t, length: size_t) -> int32_t {
    let nums = unsafe { slice::from_raw_parts(data, length as usize) };
    nums.iter().fold(0, |acc, i| acc + i)
}

也就是说,您使用的是保证匹配的C类型,然后将指针和长度转换为Rust知道如何处理的内容.

我不是Pythonista,但这个拼凑起来的代码(在How do I convert a Python list into a C array by using ctypes?的帮助下)似乎与我上面提到的 rust 迹有关:

import ctypes

lib = ctypes.cdll.LoadLibrary("./target/debug/libpython.dylib")
lib.my_func.argtypes = (ctypes.POINTER(ctypes.c_int32), ctypes.c_size_t)

list_to_sum = [1,2,3,4]
c_array = (ctypes.c_int32 * len(list_to_sum))(*list_to_sum)
print lib.my_func(c_array, len(list_to_sum))

当然,您可能希望包装它,以便更好地为代码的调用者服务.

Rust相关问答推荐

如何在Rust中为具有多个数据持有者的enum变体编写文档 comments ?

将此字符串转换为由空格字符分隔的空格

在rust中如何修改一个盒装函数并将其赋回?

何时可以在Rust中退出异步操作?

将PathBuf转换为字符串

使用极点数据帧时,找不到枚举结果的方法lazy()

如何在Rust中将选项<;选项<;字符串>;转换为选项<;选项&;str>;?

创建Rust中元对象协议的动态对象 Select /重新分配机制

如何将映射反序列化为具有与键匹配的字段的定制 struct 的向量?

在Rust中,Box:ed struct 与普通 struct 在删除顺序上有区别吗?

考虑到Rust不允许多个可变引用,类似PyTorch的自动区分如何在Rust中工作?

为什么`tokio::main`可以直接使用而不需要任何导入?

从字节数组转换为字节元组和字节数组时,为什么 Transmute 会对字节重新排序?

如何正确使用git2::Remote::push?

Rust 为什么被视为borrow ?

我可以在 Rust 中 serde struct camel_case 和 deserde PascalCase

没有得到无法返回引用局部变量`queues`的值返回引用当前函数拥有的数据的值的重复逻辑

不能将 `*self` borrow 为不可变的,因为它也被borrow 为可变的 - 编译器真的需要如此严格吗?

Rust 将特性传递给依赖项

将数据序列化为 struct 模型,其中两个字段的数据是根据 struct 中的其他字段计算的