Rust 当需要安全函数时,如何传递不安全函数而不包装该不安全函数?

Rust 当需要安全函数时,如何传递不安全函数而不包装该不安全函数?,rust,ffi,Rust,Ffi,我想将pthread\u create指向我稍后链接到的C函数。该C函数将使用pthread\u cleanup\u push和pthread\u cleanup\u pop,它们是C宏,因此不能移植到Rust 这是我的代码: extern crate libc; use std::ptr::null_mut; use libc::c_void; extern "C" { fn thr_fn1(arg:*mut c_void) -> *mut c_void; } fn main

我想将
pthread\u create
指向我稍后链接到的C函数。该C函数将使用
pthread\u cleanup\u push
pthread\u cleanup\u pop
,它们是C宏,因此不能移植到Rust

这是我的代码:

extern crate libc;
use std::ptr::null_mut;
use libc::c_void;

extern "C" {
    fn thr_fn1(arg:*mut c_void) -> *mut c_void;
}

fn main() {
    let mut tid1 = std::mem::zeroed();
    libc::pthread_create(&mut tid1, null_mut(), thr_fn1, null_mut());
}
我想既然我调用了libc的FFI,我可以直接指向一个外部C函数,但是我得到了一个错误:

错误[E0308]:类型不匹配
-->src/bin/11 threads/f05 thread cleanup.rs:25:49
|
25 | libc::pthread_create(&mut tid1,null_mut(),thr_fn1,null_mut());
|^^^^^^^^预期为正常fn,发现不安全fn
|
=注意:预期类型为`extern“C”fn(*mut libc::C_void)->*mut libc::C_void`
找到类型`不安全的外部“C”fn(*mut libc::C_void)->*mut libc::C_void{thr_fn1}`

我可以编写一个在
不安全的{}
块中调用C函数的包装器,但是有什么方法可以避免吗?

libc函数定义是错误的:C头
/usr/include/pthread.h

extern int pthread_create (pthread_t *__restrict __newthread,
                           const pthread_attr_t *__restrict __attr,
                           void *(*__start_routine) (void *),
                           void *__restrict __arg) __THROWNL __nonnull ((1, 3));
bindgen
生成此函数定义:

pub fn pthread_create(arg1: *mut pthread_t,
                      arg2: *const pthread_attr_t,
                      arg3: Option<unsafe extern "C" fn(arg1: *mut c_void) -> *mut c_void>,
                      arg4: *mut c_void) -> c_int;
pub fn pthread\u create(arg1:*mut pthread\u t,
arg2:*常量pthread\u attr\t,
arg3:选项*mut c_void>,
arg4:*mut c_void)->c_int;
它可以在macOS和Linux上编译。我不确定
选项在这里是否是个好主意;为什么会有人启动一个不调用函数的线程


我打开来纠正这个问题(没有
选项
部分)

这听起来像是libc的一个bug。您是否考虑过归档或要求他们更改签名?如果您使用锈迹代码调用要向其传递C函数的C代码,则会导致另一种选择:只需从C代码调用
pthread\u create
,跳过整个歌曲和舞蹈。只需将安全代码传递到C看起来是多余的,但是一个简单的解决方法是创建一个
extern“C”fn wrapped_fn1(arg:*mut C_void)->*mut C_void{safe{thr_fn1(arg)}
并将
wrapped_fn1
传递给
pthread_create
。也,您需要在
main
中使用
unsafe
块来调用
libc::pthread_create
std::mem::zeroed
。我不确定
选项在这里是否是一个好主意,因为bindgen是一个自动工具,无法从函数签名中辨别指针是否应该被删除
NULL
@Shepmaster所以您也建议不要在这里使用
选项,对吧?很难说。我不明白为什么会这样,但是像这样的调用经常充斥着边缘案例argumnets的重载行为。例如,
kill(2)
允许使用PID 0作为参数,即使没有进程可以使用PID 0。此发现似乎与问题中报告的错误无关。如果
arg3
是一个
选项
,则应该有一个类似“预期选项,已找到”的错误,而不是“预期正常fn,已找到不安全fn”。@Shepmaster我浏览了手册页和我的unix书籍中的部分(unix环境中的高级编程)如果给fn参数(第三个参数)赋
null
,它没有指定会发生什么。在测试时,它在Linux上线程启动时出现segfault,在OSX上它“仅”在
pthread_join
时出现segfault,所以我可以说,它永远不应该是
Null