Aka*_*all 11 python ctypes rust
我正在学习如何在Python中嵌入Rust函数,如果我的输入是ints而不是list ,一切正常.
如果我的lib.rs文件是:
#[no_mangle]
pub extern fn my_func(x: i32, y: i32) -> i32 {
return x + y;
}
Run Code Online (Sandbox Code Playgroud)
我可以使用如下:
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
Run Code Online (Sandbox Code Playgroud)
但是,如果我将我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;
}
Run Code Online (Sandbox Code Playgroud)
我不能再在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
Run Code Online (Sandbox Code Playgroud)
原因是,虽然这可行,但Python list和Rust Vec都是动态数组,但显然我在这里缺少一些东西......
为什么我的尝试不起作用?我该怎么做才能解决这个问题?
She*_*ter 14
不要这样做:
#[no_mangle]
pub extern fn my_func(my_vec: Vec<i32>) -> i32 { ... }
Run Code Online (Sandbox Code Playgroud)
基本上,你从来没有接受还是在返回任意锈病对象extern的功能,唯一的是Repr.相反,你应该接受C代表的东西.正如6502所说,这个特殊情况的最佳想法是接受指针和长度.
Rust Vec在概念上是指向数据,计数和容量的指针.您可以Vec通过添加或删除对象来修改a ,这可能导致重新分配.这是非常糟糕的,因为Python和Rust可能使用彼此不兼容的不同分配器.Segfaults就是这样!你真的想要一个切片.
相反,在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)
}
Run Code Online (Sandbox Code Playgroud)
也就是说,您正在使用保证匹配的C类型,然后将指针和长度转换为Rust知道如何处理的内容.
我不是Pythonista,但这个拼凑在一起的代码(在如何通过使用ctypes将Python列表转换为C数组的帮助下?)似乎与我上面的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))
Run Code Online (Sandbox Code Playgroud)
当然,您可能希望将其包装起来,以使代码的调用者更好.
ctypes 关于C绑定,在C中没有动态数组这样的东西.
可以传递给C函数的最近对象是指向整数的指针,但不是动态数组,因为
传递指针的一个简单替代方法(并且非常小心不要超过大小)可以使用基于函数的API.
例如:
getNumberOfThings() -> number
getThing(index) -> thing
Run Code Online (Sandbox Code Playgroud)
但Python代码会变得像
def func():
n = getNumberOfThings()
return [getThing(i) for i in range(n)]
Run Code Online (Sandbox Code Playgroud)
对应物(传递可变数量的元素)将是
def func2(L):
setNumberOfThings(len(L))
for i, x in enumerate(L):
setThing(i, x)
Run Code Online (Sandbox Code Playgroud)