2015-05-18 55 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)