我有一个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
来调用它们
我想从Python调用第二组Rust函数。这些函数略有不同,因为它们对向量(而不是切片)采用可变引用:
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()
时出现了分段错误。
经过一番调查后,我可以说以下内容:
println!()
或myotherfunction()
中的任意位置添加ffi_myotherfunction()
宏,可使该功能正常运行。输出符合预期。myotherfunction()
,而是来自Vec::from_raw_parts()
的来电。例如,我可以从myotherfunction()
注释掉对ffi_myotherfunction()
的调用,只留下不安全的块,并且仍会发生段错误。因此slice::from_raw_parts_mut()
和Vec::from_raw_parts()
之间似乎存在差异。
但我无法理解导致该段错误的原因。我错过了什么吗?难道我做错了什么? numpy如何存储数据会有问题吗?或者也许是关于终身,借用或任何其他生锈概念的东西我还没有得到?
谢谢!
答案 0 :(得分:5)
您应该只对使用Rust的分配器在Rust代码中分配的数据使用Vec::from_raw_parts
。其他任何事情都是不安全的。
我希望Python使用malloc,但Rust使用jemalloc。如果jemalloc被指示释放一个未被jemalloc分配的地址,我相信它会崩溃。因此,如果向量被释放(即它超出范围,运行其析构函数)或者如果需要重新分配(例如,如果将元素推入其中),则会遇到崩溃。
因此,第一个问题是Vec
正在运行析构函数;可以通过在完成后调用std::mem::forget(to_sort)
进行修改。另一个问题是任何重新分配都会崩溃,这个问题要危险得多;你已经基本上做到了你不能安全地访问你的向量,并且必须非常谨慎对待你做的任何事情。实际上,您应该假设Vec
上的所有都会导致崩溃。如果您想要的话可以使用&mut [T]
,那么您应该使用function send_download($file)
{
$basename = basename($file);
$length = sprintf("%u", filesize($file));
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $basename . '"');
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $length);
set_time_limit(0);
readfile($file);
}
。