我有一个Rust(1.2)库,我想使用Rust的FFI从Python(3.4)中使用它的函数 . 我在OSX 10.10优胜美地 .
几乎所有函数都将可变切片引用作为输入:
pub fn myfunction<T>(array: &mut [T]) { ... }
然后,我将此函数暴露在除锈之外:
#[no_mangle]
pub extern fn ffi_myfunction(array_pointer: *const libc::int8_t, n: libc::size_t) {
assert!(!array_pointer.is_null());
assert!(n != 0);
let mut to_sort = unsafe {
slice::from_raw_parts_mut(array_pointer as *mut i8, n as usize)
};
myfunction(&mut to_sort);
}
这很好用:使用python的ctypes模块我可以用numpy数组调用 ffi_myfunction()
:
#!/usr/bin/env python3
import ctypes
import numpy as np
rustlib = ctypes.CDLL("target/debug/libmylib.dylib")
array = np.arange(5, dtype=np.int8)
rustlib.ffi_myfunction(ctypes.c_void_p(array.ctypes.data), len(array))
我也有 libc::int16_t
, libc::int32_t
和 libc::int64_t
的生锈实现,我可以用 np.int16
, np.int32
和 np.int64
来调用它们 .
我有第二组Rust函数,我想从Python调用 . 这些函数略有不同,因为它们对向量(而不是切片)采用可变引用:
pub fn myotherfunction<T>(array: &mut Vec<T>) { ... }
然后我创建我的包装器:
#[no_mangle]
pub extern "C" fn ffi_myotherfunction(array_pointer: *const libc::int8_t, n: libc::size_t) {
assert!(!array_pointer.is_null());
assert!(n != 0);
let mut to_sort = unsafe {
Vec::from_raw_parts(array_pointer as *mut i8, n as usize, n as usize)
};
myotherfunction(&mut to_sort);
}
不幸的是,当从Python调用 ffi_myotherfunction()
时,我遇到了分段错误 .
经过一番调查,我可以说如下:
-
在
myotherfunction()
或ffi_myotherfunction()
中的任何位置添加println!()
宏使该函数正常执行 . 输出符合预期 . -
使用任何整数大小的锈库segfaults(尝试了8,16,32和64位整数) .
-
segfault似乎不是来自
myotherfunction()
,而是来自Vec::from_raw_parts()
的调用 . 例如,我可以从ffi_myotherfunction()
注释掉对myotherfunction()
的调用,只留下不安全的块,并且仍然会发生段错误 .
所以 slice::from_raw_parts_mut()
和 Vec::from_raw_parts()
之间似乎有区别 .
但我无法理解导致该段错误的原因 . 我错过了什么吗?难道我做错了什么? numpy如何存储数据会有问题吗?或者也许是关于生命,借用或任何其他生锈概念的东西,我没有得到?
谢谢!
1 回答
您应该只使用
Vec::from_raw_parts
与Rust的分配器在Rust代码中分配的数据 . 其他任何事情都是不安全的 .我希望Python使用malloc,但Rust使用jemalloc . 如果jemalloc被指示释放一个未被jemalloc分配的地址,我相信它会崩溃 . 因此,如果向量被释放(即它超出范围,运行其析构函数)或者如果需要重新分配(例如,如果将元素推到其上),则会遇到崩溃 .
因此,第一个问题是
Vec
正在运行析构函数;当你完成它时,可以通过调用std::mem::forget(to_sort)
进行修改 . 另一个问题是任何重新分配都会崩溃,这个问题要危险得多;你基本上已经做到了你不能安全地访问你的向量,并且必须非常小心你用它做什么 . 真的,你应该假设Vec
上的所有内容都会崩溃 . 你应该使用&mut [T]
,如果你想要的话可以使用它 .