将 Option<&mut T> 转换为 *mut T

Bri*_*ian 7 pointers ffi rust

我正在围绕 C 库编写一个 Rust 包装器,在这样做的同时,我试图利用The Book中提到的“可空指针优化” ,但我找不到转换为Option<&T>*const T的好方法Option<&mut T>喜欢*mut T他们所描述的内容。

\n\n

我真正想要的是能够打电话Some(&foo) as *const _。不幸的是,这不起作用,所以我能想到的下一个最好的事情是Option<T>使我能够调用Some(&foo).as_ptr(). 以下代码是该特征的工作定义和实现:

\n\n
use std::ptr;\n\ntrait AsPtr<T> {\n    fn as_ptr(&self) -> *const T;\n}\n\nimpl<\'a, T> AsPtr<T> for Option<&\'a T> {\n    fn as_ptr(&self) -> *const T {\n        match *self {\n            Some(val) => val as *const _,\n            None => ptr::null(),\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在我可以打电话Some(&foo).as_ptr()来获取一个*const _,我希望能够打电话Some(&mut foo).as_ptr()来获取一个*mut _。以下是我为此创建的新特征:

\n\n
trait AsMutPtr<T> {\n    fn as_mut_ptr(&self) -> *mut T;\n}\n\nimpl<\'a, T> AsMutPtr<T> for Option<&\'a mut T> {\n    fn as_mut_ptr(&self) -> *mut T {\n        match *self {\n            Some(val) => val as *mut _,\n            None => ptr::null_mut(),\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

问题是,该AsMutPtr特征无法编译。当我尝试时,出现以下错误:

\n\n
use std::ptr;\n\ntrait AsPtr<T> {\n    fn as_ptr(&self) -> *const T;\n}\n\nimpl<\'a, T> AsPtr<T> for Option<&\'a T> {\n    fn as_ptr(&self) -> *const T {\n        match *self {\n            Some(val) => val as *const _,\n            None => ptr::null(),\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我没有看到这两个特征之间发生了什么变化导致它失败 \xe2\x80\x94 我不认为添加mut会产生那么大的差异。我尝试添加一个ref,但这只会导致不同的错误,而且我不希望需要它。

\n\n

为什么这个特质不起作用AsMutPtr

\n

Vla*_*eev 5

不幸的是,编写trait impl for&mut T而不是&T 确实会产生很大的差异。&mut T与 相反&T,不是Copy,因此您不能直接从共享引用中提取它:

& &T      --->  &T
& &mut T  -/->  &mut T
Run Code Online (Sandbox Code Playgroud)

这是相当自然的——否则可变引用的别名是可能的,这违反了 Rust 借用规则。

你可能会问这个外层&是从哪里来的。它实际上来自于方法&selfas_mut_ptr()。如果您对某个东西有不可变的引用,即使该东西内部包含可变引用,您也无法使用它们来改变它们背后的数据。这也违反了借用语义。

不幸的是,我认为没有办法在不安全的情况下做到这一点。您需要&mut T“按值”才能将其转换为*mut T,但无法通过共享引用“按值”获取它。因此,我建议您使用ptr::read()

use std::ptr;

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> {
    fn as_mut_ptr(&self) -> *mut T {
        match *self {
            Some(ref val) => unsafe { ptr::read(val) as *mut _ },
            None => ptr::null_mut(),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

val这是& &mut T因为ref模式中存在限定符,因此ptr::read(val)返回&mut T,为可变引用别名。我认为如果它立即转换为原始指针并且不会泄漏就可以了,但即使结果是原始指针,它仍然意味着您有两个别名可变指针。您应该非常小心地对待它们。

或者,您可以修改AsMutPtr::as_mut_ptr()以按值消耗其目标:

trait AsMutPtr<T> {
    fn as_mut_ptr(self) -> *mut T;
}

impl<'a, T> AsMutPtr<T> for Option<&'a mut T> {
    fn as_mut_ptr(self) -> *mut T {
        match self {
            Some(value) => value as *mut T,
            None => ptr::null_mut()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,在这种情况下Option<&mut T>将被消耗as_mut_ptr()Option<&mut T>例如,如果将其存储在结构中,则这可能不可行。我不太确定是否可以以某种方式手动执行重新借用,Option<&mut T>而不是仅仅&mut T(它不会自动触发);如果可能的话,按价值as_mut_ptr()可能是最好的整体解决方案。