我正在为一个C库编写一个Rust包装器,在这样做的同时,我试图利用书中提到的“可空指针优化”,但是我找不到像他们描述的那样将Option<&T>转换为*const T和Option<&mut T>到*mut T的好方法。
我真正想要的是能够调用Some(&foo) as *const _。不幸的是,这不起作用,所以我能想到的下一个最好的事情就是Option<T>上的一个特性,它使我能够调用Some(&foo).as_ptr()。以下代码是该特性的有效定义和实现:
use std::ptr;
trait AsPtr<T> {
fn as_ptr(&self) -> *const T;
}
impl<'a, T> AsPtr<T> for Option<&'a T> {
fn as_ptr(&self) -> *const T {
match *self {
Some(val) => val as *const _,
None => ptr::null(),
}
}
}现在我可以调用Some(&foo).as_ptr()来获取*const _了,我希望能够调用Some(&mut foo).as_ptr()来获得*mut _。以下是我做这件事的新特点:
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(val) => val as *mut _,
None => ptr::null_mut(),
}
}
}问题是,AsMutPtr特性无法编译。当我尝试时,我会得到以下错误:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:22:15
|
22 | match *self {
| ^^^^^
| |
| cannot move out of borrowed content
| help: consider removing the `*`: `self`
23 | Some(val) => val as *mut _,
| --- data moved here
|
note: move occurs because `val` has type `&mut T`, which does not implement the `Copy` trait
--> src/lib.rs:23:18
|
23 | Some(val) => val as *mut _,
| ^^^我看不出导致它失败的两个特征之间发生了什么变化--我不认为添加mut会带来很大的不同。我试着添加了一个ref,但是这只会导致一个不同的错误,而且我也不希望这样做。
为什么AsMutPtr特性不起作用?
发布于 2016-03-09 10:01:26
不幸的是,为&mut T而不是&T编写这个特性确实带来了很大的不同。与&mut T相比,&T不是Copy,因此不能直接从共享引用中提取它:
& &T ---> &T
& &mut T -/-> &mut T这是相当自然的--否则就有可能对可变引用进行别名,这违反了锈蚀借用规则。
您可能会问,外部&来自何处。它实际上来自于&self中的as_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(),
}
}
}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()
}
}
}但是,在本例中,Option<&mut T>将由as_mut_ptr()使用。例如,如果这个Option<&mut T>存储在一个结构中,这可能是不可行的。我不太确定是否可以使用Option<&mut T> (而不是只使用&mut T )手动执行重新借款(不会自动触发);如果有可能,那么按值计算的as_mut_ptr()可能是最好的整体解决方案。
发布于 2016-03-10 22:02:06
问题是,您正在从&mut中读取&,但是&muts不是Copy,所以必须移动--而且不能从const引用中移出。这实际上解释了弗拉基米尔·马特维耶夫( Vladimir )从更基本的属性角度对&&mut → &的洞察力。
这其实是比较简单的解决办法。如果您可以读取*const _,则可以读取*mut _。这两者是相同的类型,除了一个标志,上面写着“小心,这是共享的”。因为不管是哪种方式,取消都是不安全的,因此实际上没有理由阻止您在两者之间穿插。
所以你真的可以
match *self {
Some(ref val) => val as *const _ as *mut _,
None => ptr::null_mut(),
}读取不可变引用,使其成为不可变指针,然后使其成为可变指针。另外,这一切都是通过安全锈蚀完成的,所以我们知道我们没有违反任何别名规则。
也就是说,在*mut引用消失之前实际使用&mut指针可能是个坏主意。我会对此犹豫不决,并试图重新考虑您的包装,以更安全的东西。
发布于 2016-03-09 08:11:22
这能满足你的期望吗?
trait AsMutPtr<T> {
fn as_mut_ptr(self) -> *mut T;
}
impl<T> AsMutPtr<T> for Option<*mut T> {
fn as_mut_ptr(self) -> *mut T {
match self {
Some(val) => val as *mut _,
None => ptr::null_mut(),
}
}
}https://stackoverflow.com/questions/35885670
复制相似问题