我有一个返回array
的Rust函数,我想在Python
中使用这个数组,它可能是list
或numpy.array
它并不重要
My Rust功能如下所示:
#[no_mangle]
pub extern fn make_array() -> [i32; 4] {
let my_array: [i32; 4] = [1,2,3,4];
return my_array;
}
我试图用Python这样称呼它:
In [20]: import ctypes
In [21]: from ctypes import cdll
In [22]: lib = cdll.LoadLibrary("/home/user/RustStuff/embed/target/release/libembed.so")
In [23]: lib.make_array.restype = ctypes.ARRAY(ctypes.c_int32, 4)
In [24]: temp = lib.make_array()
In [25]: [i for i in temp]
Out[25]: [1, 2, -760202930, 32611]
我做错了什么?为什么我的输出不是[1,2,3,4]
?为什么我的前两个元素是正确的,另外两个元素是垃圾填充?
我无法在ctypes.ARRAY
找到任何好的文档,所以我只是选择了正确的内容,这很可能就是问题所在。
答案 0 :(得分:5)
正如其他人所说,你无法真正正确地返回一个固定大小的数组。但是你可以通过将数组包装在一个结构中来欺骗 ctypes做正确的事情:
import ctypes
class Int32_4(ctypes.Structure):
_fields_ = [("array", ctypes.c_int32 * 4)]
lib = ctypes.CDLL("embed.dll")
lib.make_array.restype = Int32_4
temp = lib.make_array()
print(temp.array[:])
这导致我的计算机上出现[1, 2, 3, 4]
。
附录:这是一个"技巧"因为我们正在利用C可以做什么和Rust可以做什么之间的差异。 C 不会允许您按值返回固定大小的数组,但Rust 将,它的工作方式与返回用户定义的结构相同。
因此,我们做了一些C 将允许的事情:返回一个恰好包含固定大小数组的结构。这个很好,它与Rust正在使用的布局相匹配。
当然,这也有些愚蠢,因为我并非完全确信这是明确定义的行为。如果您想要更加安全,可以在Rust端更改返回类型以匹配C:
#[repr(C)]
struct Int32_4 {
array: [i32; 4]
}
答案 1 :(得分:1)
我同意@delnan所说的内容 - you can't return fixed-size arrays in C。一个主要的不兼容性是Rust数组知道它们的大小,但C数组不知道。您将需要遵守每个其他C程序如何完成此操作 - 分别返回指针和长度。相比之下,Isn&#t; t Rust是一种不错的现代语言吗?
我偷了并修改了一些Python代码from another answer
import ctypes
from ctypes import cdll
lib = cdll.LoadLibrary("libarray.dylib")
lib.make_array.restype = ctypes.POINTER(ctypes.c_int32 * 4)
print [i for i in lib.make_array().contents]
这适用于此Rust代码:
static ARRAY: [i32; 4] = [1,2,3,4];
#[no_mangle]
pub extern fn make_array() -> *const i32 {
ARRAY.as_ptr()
}
在这里,我们正在做最简单的事情,创建一个数组,它将在程序的整个长度内存活并返回对其数据的引用。在您的真实程序中,您可能需要更加谨慎,以确保您的Vec<i32>
或&[i32]
严格超过Python代码指针的长度,否则会导致内存损坏。