方便地访问原始指针的成员



对于我们知道不需要检查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);
}

依据

最新更新