如何在传递int数组时修复分段错误?

big*_*gie 2 python numpy ffi segmentation-fault rust

我有一个Rust(1.2)库,我想使用Rust的FFI从Python(3.4)中使用它的函数.我在OSX 10.10优胜美地.

几乎所有函数都将可变切片引用作为输入:

pub fn myfunction<T>(array: &mut [T]) { ... }
Run Code Online (Sandbox Code Playgroud)

然后,我将此函数暴露在除锈之外:

#[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);
}
Run Code Online (Sandbox Code Playgroud)

这很好用:使用python的ctypes模块我可以ffi_myfunction()使用numpy数组调用:

#!/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))
Run Code Online (Sandbox Code Playgroud)

我也有生锈的实施libc::int16_t,libc::int32_t以及libc::int64_t和我可以给他们打电话np.int16,np.int32np.int64.

我有第二组Rust函数,我想从Python调用.这些函数略有不同,因为它们对向量(而不是切片)采用可变引用:

pub fn myotherfunction<T>(array: &mut Vec<T>) { ... }
Run Code Online (Sandbox Code Playgroud)

然后我创建我的包装器:

#[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);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,ffi_myotherfunction()从Python 调用时我遇到了分段错误.

经过一番调查,我可以说如下:

  1. println!()任意位置添加宏myotherfunction()ffi_myotherfunction()使功能正常执行.输出符合预期.
  2. 对于使用的任何整数大小,锈库都会出现段错误(尝试了8位,16位,32位和64位整数).
  3. 段错似乎不是myotherfunction()来自于呼叫Vec::from_raw_parts().例如,我可以注释掉调用myotherfunction()ffi_myotherfunction(),只留下了不安全的块中,将段错误仍然发生.

因此,似乎有之间的差异slice::from_raw_parts_mut()Vec::from_raw_parts().

但我无法理解导致该段错误的原因.我错过了什么吗?难道我做错了什么?numpy如何存储数据会有问题吗?或者也许是关于生命,借用或任何其他生锈概念的东西,我没有得到?

谢谢!

Chr*_*gan 5

您应该只使用Vec::from_raw_partsRust的分配器在Rust代码中分配的数据.其他任何事情都是不安全的.

我希望Python使用malloc,但Rust使用jemalloc.如果jemalloc被指示释放一个未被jemalloc分配的地址,我相信它会崩溃.因此,如果向量被释放(即它超出范围,运行其析构函数)或者如果需要重新分配(例如,如果将元素推到其上),则会遇到崩溃.

因此,第一个问题是Vec它的析构函数运行; std::mem::forget(to_sort)当你完成它时可以通过调用来修改.另一个问题是任何重新分配都会崩溃,这个问题要危险得多; 你基本上已经做到了你不能安全地访问你的向量,并且必须非常小心你用它做什么.真的,你应该假设一切都Vec将崩溃.&mut [T]如果您想要的话可​​以使用它,那么您应该使用它.