2016-10-28 1 views
1

우리가 오히려 어색 할 수 있습니다 NULL에 대해 검사 할 필요가 없습니다 알고 인스턴스에 대한 원시 포인터의 중첩 된 멤버에 액세스하기위한 표기법 :원시 포인터 구성원에게 편리하게 액세스 할 수 있습니까?

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

이 원시 포인터의 구조체 멤버가 필요없이 액세스 할 수 있습니다 명시 적으로 각 시간에 참조 해제 하시겠습니까? 어쩌면 root.link().link().link() 같은 메서드를 사용하거나 형식을 래핑하여?

관용적 인 녹이 피하는 동안, 피할 수없는 예외적 인 경우가 있습니다. Rc이 메모리 오버 헤드를 가지고 있고, 빌린 체커가 인터 - 링크 멤버들에 대한 문제를 일으키고, C-API가 포인터를 필요로 할 수 있습니다 ... 등등.

+0

래퍼 방법 접근 방법을 사용해 보셨습니까? 나는 또한 그 가장자리 사건에 대해 매우 궁금합니다. –

+0

@ E_net4, 나는 그것을 아직 시도하지 않았지만 (어떻게 쓰여질 지 확신 할 수는 없다), 가장 유망한 해결책처럼 보인다. – ideasman42

+0

"[녹슨 포인터]는 NULL에 대해 검사 할 필요가 없다"고 말하는 것은 잘못되었습니다. 참조에는 해당 등록 정보 (null 일 수 없음)가 있습니다. 이는 차용 검사기가 적용되어 (매달린 참조를 방지하기 위해) 이유입니다. 반면에 포인터는 null이 될 수 있지만, 안전성 검사는 '안전하지 않은'블록 밖에서 참조 해제 될 수 없다는 점에서 다릅니다. – 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이 아닌 것을 건설에 주장, 그래서 우리는 역 참조 할 때 다시 확인 할 필요가 없습니다. 당신은 어떤 다른 녹 유형과 정확히 같은 방법으로 원시 포인터에 대한 사용자 정의 메소드를 구현할 수

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

래퍼 메소드는 실제로 그 코드의 가독성을 향상시킬 수 있습니다. 그냥 The Book에 따라 :

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

Gist

1

: 메소드를 호출하거나 속성에 액세스 할 때

그런 다음 우리는 Deref/ DerefMut의 언어 체크를하자 :

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() 
관련 문제