Pass Python List To Embedded Rust Function
Solution 1:
Don't do this:
#[no_mangle]pubexternfnmy_func(my_vec: Vec<i32>) ->i32 { ... }
You basically never want to accept or return an arbitrary Rust object in an extern
function, only ones that are Repr
. Instead, you should accept something that is representable by C. As 6502 says, the best idea for this particular case would be to accept a pointer and a length.
Rust's Vec
is conceptually a pointer to data, a count, and a capacity. You are able to modify a Vec
by adding or removing objects, which can cause reallocation to happen. This is doubly bad because it is likely that Python and Rust use different allocators that are not compatible with each other. Segfaults lie this way! You really want a slice.
Instead, do something like this on the Rust side:
externcrate libc;
use libc::{size_t,int32_t};
use std::slice;
#[no_mangle]pubexternfnmy_func(data: *const int32_t, length: size_t) -> int32_t {
letnums = unsafe { slice::from_raw_parts(data, length asusize) };
nums.iter().fold(0, |acc, i| acc + i)
}
Namely, you are using the guaranteed-to-match C types, and then converting the pointer and length to something Rust knows how to deal with.
I'm no Pythonista, but this cobbled-together code (with help from How do I convert a Python list into a C array by using ctypes?) seems to work with the Rust I have above:
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))
Of course, you probably want to wrap that to make it nicer for the caller of your code.
Solution 2:
ctypes
is about C bindings and in C there's no such a thing as a dynamic array.
The closest object you can pass to a C function is a pointer to integer that however is not a dynamic array because
- It doesn't carry the size information
- You cannot grow or shrink the area, just access existing elements
A simple alternative to passing pointers (and being very careful about not getting past the size) you could use instead is a function-based API.
For example:
getNumberOfThings() -> number
getThing(index) -> thing
but the Python code would then become like
def func():
n = getNumberOfThings()
return [getThing(i) for i in range(n)]
The counterpart (passing a variable number of elements) would be
def func2(L):
setNumberOfThings(len(L))
for i, x in enumerate(L):
setThing(i, x)
Post a Comment for "Pass Python List To Embedded Rust Function"