UnsafeCell
documentation说
UnsafeCell<T>
类型是获取被视为可变的可别名数据的唯一合法方法。
唯一的构造方法是:
pub const fn new(value: T) -> UnsafeCell<T>
但是,无法创建c_void
,我们只能创建*mut c_void
或*const c_void
。
是否可以从UnsafeCell<c_void>
创建*mut c_void
?这样,我们就可以让编译器知道指针可以指向可变的东西。
还是没有必要?即使我们知道某些FFI调用会突变它指向的数据并且有多个引用,也可以始终使用*mut c_void
吗?
一个用例将是:
struct FFIStruct { v: UnsafeCell<c_void>, other_fields: ... }
impl FFIStruct {
// We don't want to require &mut self, as we
// are sure private call_ffi() will always be called
// sequentially, and we don't want to stop
// status() being callable during the call
fn call_ffi(&self){ ffi_function(self.v.get()) }
pub fn status(&self) -> FFIStatus { ... }
}
现在我们如何创建FFIStruct
?还是只使用*mut c_void
就可以了?
&Cell<c_void>
需要#![feature(as_cell)]
:
unsafe fn get_cell<'a>(p: *mut c_void) -> &'a Cell<c_void> {
Cell::from_mut(&mut *p)
}
答案 0 :(得分:3)
TL; DR:只需使用*mut Foo
。在这里 不需要任何类型的单元格。
免责声明:目前还没有正式的Rust内存模型。
您无法创建这种类型的句点,因为您无法 1 创建c_void
的实例。
问题是,您不需要创建这样的类型。 Aliasing is not spatial but temporal。您可以有多个*mut T
指向同一地点,直到您尝试访问一个地点都没关系。实质上,这会将其转换为参考,并且在该参考存在时需要保持别名要求。
原始指针不属于Rust的安全内存模型。
不同于引用和智能指针,原始指针:
- 允许通过具有不变和可变的指针或指向同一位置的多个可变的指针来忽略借用规则
- 不能保证指向有效内存
- 允许为空
- 不执行任何自动清理
另请参阅:
1 从技术上讲,您可以,但这仅仅是因为实现和向后兼容的限制。
答案 1 :(得分:-1)
在some internal discussion in the Rust forum和a discussion on RFC 1861之后,我意识到c_void
只是一种常见的解决方法,并且存在其他选项,例如struct Opaque<UnsafeCell<()>>
。
因此,我得出的结论是,这里需要的是*const UnsafeCell<c_void>
。根据其类型,我们知道:
const
,这意味着对UB *mut T
的任何强制转换都将由UB和程序员来避免。这样可以保护它指向在Rust中被修改的内存(除非通过UnsafeCell
,原因); UnsafeCell
,因此它意味着内部可变性。这证明FFI功能能够对其进行更改; c_void
,无法创建UnsafeCell<c_void>
。它们只能由FFI创建。use std::cell::UnsafeCell;
use std::os::raw::c_void;
//Let's say all those functions are in an FFI module,
//with the exact same behaviour
fn ffi_create() -> *mut c_void {
Box::into_raw(Box::new(0u8)) as *mut c_void
}
unsafe fn ffi_write_u8(p: *mut c_void, v:u8) {
*(p as *mut u8) = v;
}
unsafe fn ffi_read_u8(p: *mut c_void) -> u8 {
*(p as *mut u8)
}
fn main() {
unsafe {
//let's ignore ffi_destroy() for now
let pointer = ffi_create() as *const UnsafeCell<c_void>;
let ref_pointer = &pointer;
ffi_write_u8((&*pointer).get(), 7);
let integer = ffi_read_u8((&**ref_pointer).get());
assert_eq!(integer, 7);
}
}
有趣的是,在*mut c_void
和*const UnsafeCell<c_void>
之间进行转换是多么容易且符合人体工程学。