2015-05-18 30 views
10

我正在学习如何在Python中嵌入Rust函数,并且如果我的输入是int s,但没有列表,则一切正常。将Python列表传递给嵌入的Rust函数

如果我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和锈病的Vec是两个动态数组,但显然我在这里失去了一些东西......

为什么我尝试不工作?我应该怎么做才能解决它?

回答

13

别t这样做:

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

你基本上从未要接受或在extern函数返回一个任意锈对象,只有那些有Repr。相反,你应该接受C表示的东西。作为6502 says,这个特定情况的最佳想法是接受一个指针和一个长度。

Rust的Vec概念上指向数据,计数,和容量。您可以通过添加或删除对象来修改Vec,这会导致重新分配发生。这是非常糟糕的,因为Python和Rust可能使用不同的分配器,这些分配器彼此不兼容。 Segfaults就是这样的!你真的想要一个

相反,做锈侧是这样的:

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型,然后将指针和长度的东西锈知道如何处理用。

我不是Pythonista,但这种玩票码(从How do I convert a Python list into a C array by using ctypes?帮助)似乎与防锈工作,我有以上:

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)) 

当然,你可能想换行做它更适合你的代码的调用者。

5

​​是关于C绑定,在C中没有动态数组这样的事情。

你可以把信息传给C函数最接近的对象然而,整数的指针是不是一个动态数组,因为

  1. 它不携带大小的信息
  2. 不能增长或收缩区域,只需访问现有元素

您可以使用的一种简单替代方法是使用基于函数的API来传递指针(并且非常小心不要超过大小)。

例如:

getNumberOfThings() -> number 
getThing(index) -> thing 

但随后Python代码会成为像

def func(): 
    n = getNumberOfThings() 
    return [getThing(i) for i in range(n)] 

对方(通过可变数量的元件)会

def func2(L): 
    setNumberOfThings(len(L)) 
    for i, x in enumerate(L): 
     setThing(i, x)