对于我们知道不需要检查NULL的实例,访问原始指针嵌套成员的符号可能相当尴尬:
struct MyLink {
link: *mut MyLink,
}
let var = *(*(*(*root).link).link).link;
可以访问原始指针的结构成员,而不必每次显式地取消引用?也许通过使用像root.link().link().link()
这样的方法,或者通过包装类型?
虽然习惯Rust会避免这种情况,但在一些特殊情况下,这种情况并不容易避免。Rc
有内存开销,借用检查器导致成员间连接问题,C-API可能需要指针…等。
如果这是您的代码中反复出现的情况,我将只创建一个通用包装器。
#[repr(C)]
#[derive(Hash)]
struct Ptr<T> {
ptr: *mut T
}
impl<T> Ptr<T> {
pub unsafe fn new(ptr: *mut T) -> Ptr<T> {
debug_assert!(!ptr.is_null());
Ptr { ptr: ptr }
}
#[inline(always)]
pub fn as_pointer(&self) -> *mut T {
self.ptr
}
}
impl<T> Deref for Ptr<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T> DerefMut for Ptr<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
impl<T> Copy for Ptr<T> { }
impl<T> Clone for Ptr<T> {
#[inline(always)]
fn clone(&self) -> Ptr<T> { *self }
}
impl<T> PartialEq for Ptr<T> {
fn eq(&self, other: &Ptr<T>) -> bool {
self.ptr == other.ptr
}
}
在构造时断言ptr
实际上不为空,因此在解引用时不必再次检查。
然后我们让语言在调用方法或访问属性时检查Deref
/DerefMut
:
struct MyLink {
link: Ptr<MyLink>,
}
fn main() {
let mut link = MyLink { link: unsafe { Ptr::new(1 as *mut _) } };
let next = MyLink { link: unsafe { Ptr::new(&mut link as *mut _) } };
let _ = next.link;
}
您可以为原始指针实现与其他Rust类型完全相同的自定义方法:
trait WickedRef<T>{
unsafe fn wicked_ref<'x>(self) -> &'x T;
}
impl<T> WickedRef<T> for *mut T{
unsafe fn wicked_ref<'x>(self) -> &'x T{
&*self
}
}
root.link.wicked_ref().link.wicked_ref()
包装器方法确实可以提高代码的可读性。只要跟随这本书:
struct MyLink {
link: *mut MyLink,
pub n: i32,
}
impl MyLink {
pub unsafe fn link(&self) -> &MyLink {
&*self.link
}
pub unsafe fn mut_link(&mut self) -> &mut MyLink {
&mut *self.link
}
}
是否将方法原型标记为unsafe
取决于您的具体情况,但实现必须在不安全块中:从指针获取引用,即使没有解引用,也是不安全的。
unsafe {
let mut l1 = MyLink {
link: 0 as *mut MyLink,
n: 4,
};
let mut l2 = MyLink {
link: &mut l1 as *mut MyLink,
n: 3,
};
let n1 = l2.n;
let n2 = l2.link().n;
println!("{} -> {}", n1, n2);
}
依据