含裸指针的结构体能否同时实现Send trait并保证FFI安全?
解决方案:手动实现
Send并保持FFI安全 你的问题核心在于两个矛盾点:裸指针*mut c_void不自动实现Send(Rust无法确认其线程安全性),而Unique指针因为没有C兼容布局,无法安全用于FFI调用。下面是解决这个问题的正确方式:
核心思路
- 保持
Storage的#[repr(C)]标注,确保结构体布局与C兼容,字段继续使用*mut c_void(这是FFI调用的必要条件)。 - 手动为
Storage实现Sendtrait,但要通过unsafe块来做——因为我们需要向Rust编译器保证:这个结构体跨线程传递是安全的。
修正后的代码
extern crate libc; use libc::{c_void, malloc, size_t}; use std::thread; const INITIAL_CAPACITY: size_t = 8; extern "C" { fn mutate(s: *mut Storage); } #[repr(C)] struct Storage { #[allow(dead_code)] buf: *mut c_void, capacity: usize, } // 手动实现Send trait:我们确认这个结构体跨线程是安全的 unsafe impl Send for Storage {} fn main() { let buf = unsafe { malloc(INITIAL_CAPACITY) }; let mut s = Storage { buf, capacity: INITIAL_CAPACITY, }; thread::spawn(move || { unsafe { mutate(&mut s); } }) .join() .unwrap(); }
关键解释
为什么手动实现Send是安全的?
Rust默认不给裸指针实现Send,因为裸指针可以指向任何内存,Rust无法判断其线程安全性。但在你的场景中:
buf是通过C的malloc分配的,POSIX标准保证malloc/realloc/free是线程安全的;- 只要你的
mutate函数逻辑正确(比如不会同时在多个线程中无同步地修改同一个Storage或其指向的内存),跨线程传递Storage就是安全的。
为什么Unique不适合这个场景?
Unique是Rust的内部智能指针,它没有#[repr(C)]标注,这意味着它的内存布局不被C语言兼容。当你把包含Unique的Storage传给C函数时,C无法正确解析buf字段的结构,会导致内存访问错误,所以编译器会抛出FFI不安全的警告。
注意事项
手动实现Send是unsafe操作,你必须自己承担保证线程安全性的责任:
- 确保同一时间只有一个线程在修改
Storage或其指向的内存; - 如果涉及多个线程访问,需要添加同步机制(比如互斥锁)。
内容的提问来源于stack exchange,提问作者Edd Barrett
相关产品推荐
相关产品推荐

