传递 int 数组时如何修复分段错误?
How to fix a segmentation fault when passing an array of int?
我有一个 Rust (1.2) 库,我想使用 Rust 的 FFI 从 Python (3.4) 中使用它的函数。我在 OSX 10.10 Yosemite.
我的几乎所有函数都将可变切片引用作为输入:
pub fn myfunction<T>(array: &mut [T]) { ... }
然后我将此函数暴露在 Rust 之外使用:
#[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
的 Rust 实现,我可以用 np.int16
、np.int32
和 [=21 调用它们=].
我想从 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()
时出现分段错误。
经过一番调查,我可以说如下:
- 在
myotherfunction()
或 ffi_myotherfunction()
中的任意位置添加 println!()
宏可使函数正常执行。输出符合预期。
- rust 库对于使用的任何整数大小都会出现段错误(尝试了 8、16、32 和 64 位整数)。
- 段错误似乎不是来自
myotherfunction()
,而是来自对 Vec::from_raw_parts()
的调用。例如,我可以注释掉 ffi_myotherfunction()
对 myotherfunction()
的调用,只留下不安全的块,段错误仍然发生。
所以slice::from_raw_parts_mut()
和Vec::from_raw_parts()
好像有区别。
但我不明白是什么导致了该段错误。我错过了什么吗?难道我做错了什么? numpy 存储数据的方式会不会有问题?或者可能是关于生命周期、借用或任何其他我没有得到的生锈概念?
谢谢!
您应该只将 Vec::from_raw_parts
用于已在 Rust 代码中使用 Rust 分配器分配的数据。其他任何东西都是不安全的。
我希望 Python 使用 malloc,但 Rust 使用 jemalloc。如果指示 jemalloc 释放一个不是由 jemalloc 分配的地址,我相信它会崩溃。因此,如果一个 vector 被释放(即它超出范围,它的析构函数是 运行)或者如果它需要重新分配(例如,如果你将一个元素推到它上面),你将遇到崩溃。
第一个问题是 Vec
有它的析构函数 运行;完成后可以通过调用 std::mem::forget(to_sort)
来修改。另一个问题,即任何重新分配都会崩溃,这要危险得多;你基本上已经做到了你不能安全地可变地访问你的向量,并且必须 非常 对你做的任何事情保持谨慎。实际上,您应该假设 Vec
上的 一切 都会发生崩溃。你应该使用 &mut [T]
来代替,如果你想要的可以用它来完成的话。
我有一个 Rust (1.2) 库,我想使用 Rust 的 FFI 从 Python (3.4) 中使用它的函数。我在 OSX 10.10 Yosemite.
我的几乎所有函数都将可变切片引用作为输入:
pub fn myfunction<T>(array: &mut [T]) { ... }
然后我将此函数暴露在 Rust 之外使用:
#[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
的 Rust 实现,我可以用 np.int16
、np.int32
和 [=21 调用它们=].
我想从 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()
时出现分段错误。
经过一番调查,我可以说如下:
- 在
myotherfunction()
或ffi_myotherfunction()
中的任意位置添加println!()
宏可使函数正常执行。输出符合预期。 - rust 库对于使用的任何整数大小都会出现段错误(尝试了 8、16、32 和 64 位整数)。
- 段错误似乎不是来自
myotherfunction()
,而是来自对Vec::from_raw_parts()
的调用。例如,我可以注释掉ffi_myotherfunction()
对myotherfunction()
的调用,只留下不安全的块,段错误仍然发生。
所以slice::from_raw_parts_mut()
和Vec::from_raw_parts()
好像有区别。
但我不明白是什么导致了该段错误。我错过了什么吗?难道我做错了什么? numpy 存储数据的方式会不会有问题?或者可能是关于生命周期、借用或任何其他我没有得到的生锈概念?
谢谢!
您应该只将 Vec::from_raw_parts
用于已在 Rust 代码中使用 Rust 分配器分配的数据。其他任何东西都是不安全的。
我希望 Python 使用 malloc,但 Rust 使用 jemalloc。如果指示 jemalloc 释放一个不是由 jemalloc 分配的地址,我相信它会崩溃。因此,如果一个 vector 被释放(即它超出范围,它的析构函数是 运行)或者如果它需要重新分配(例如,如果你将一个元素推到它上面),你将遇到崩溃。
第一个问题是 Vec
有它的析构函数 运行;完成后可以通过调用 std::mem::forget(to_sort)
来修改。另一个问题,即任何重新分配都会崩溃,这要危险得多;你基本上已经做到了你不能安全地可变地访问你的向量,并且必须 非常 对你做的任何事情保持谨慎。实际上,您应该假设 Vec
上的 一切 都会发生崩溃。你应该使用 &mut [T]
来代替,如果你想要的可以用它来完成的话。