2016-10-28 7 views
1

私たちはかなり厄介なことができNULLに対してチェックする必要はありません知っているインスタンスのために生のポインタのネストされたメンバーにアクセスするための表記法:生ポインタのメンバーに簡単にアクセスできますか?

struct MyLink { 
    link: *mut MyLink, 
} 
let var = *(*(*(*root).link).link).link; 

は生のポインタの構造体のメンバがなくてもアクセスすることができます毎回明示的に参照解除する必要がありますか?たぶんroot.link().link().link()のようなメソッドを使うか、型をラップすることで?

慣用的なRustはこれを回避しますが、回避しにくい例外的なケースがあります。 Rcにメモリオーバヘッドがあり、借用チェッカーが相互リンクメンバの問題を引き起こし、C-APIがポインタを必要とするなど...

+0

ラッパーメソッドのアプローチを試しましたか?私はまた、そのエッジケースについて非常に興味があります。 –

+0

@ E_net4、私はまだ試していませんでした(どのように書かれるかわからない)が、最も有望な解決策のようだ。 – ideasman42

+0

"[Rustのポインタ]はNULLと照合する必要はありません"とは言えません。参照にはそのプロパティがあり(nullにはできません)、そのために貸借チェッカーが適用されています(ダングリング参照を防止するため)。一方、ポインタはnullでも構いませんが、安全性チェックは 'unsafe'ブロックの外で参照解除できないことです。 – ampron

答えて

3

これはあなたのコードでは、定期的な状況がある場合は、私は、一般的なラッパーを作成したいです。

#[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が効果的にnullではないので、我々は逆参照時にもう一度チェックする必要がないことを建設すると主張しています。メソッドの呼び出しまたは属性にアクセスするとき

その後、我々は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; 
} 
+0

生のポインタを使用するのと比べて、これはビルドをリリースするためにオーバーヘッドを追加しますか? – ideasman42

+1

@ ideasman42:最適化後には使用しないでください。メソッドは、インライン化するのには些細なものです。これが心配な場合は、 'deref'と' deref_mut'の上に '#[inline(always)]を打つことができます。 –

+0

これは私が想定しているもので、 'MyLink {link:Ptr :: new(1 as * mut _)};をもっとコンパクトに書く方法を知りたいと思っています。最後の手段。 – ideasman42

1

ラッパーメソッドが実際にそのコードの可読性を向上させる可能性があります。

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かないような方法のプロトタイプをマークするが、実装が危険なブロックにする必要があるかどうか::ちょうどThe Bookに従うことにより、ポインタからの参照を取得、でも逆参照せずに、安全ではありません。それを使用して

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); 
} 

Gist

1

あなたは、他の錆タイプの場合とまったく同じように生のポインタのためのカスタムメソッドを実装することができます。

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() 
関連する問題