[pointers] Rust에서“팻 포인터”란 무엇입니까?

나는 이미 여러 문맥에서 “팻 포인터”라는 용어를 읽었지만 그것이 정확히 무엇을 의미하고 Rust에서 언제 사용되는지 잘 모르겠습니다. 포인터는 일반 포인터보다 두 배 큰 것 같지만 이유를 모르겠습니다. 또한 특성 개체와 관련이있는 것 같습니다.



답변

“팻 포인터”라는 용어는 동적 크기 유형 (DST) (슬라이스 또는 특성 개체)에 대한 참조 및 원시 포인터를 참조하는 데 사용됩니다 . 팻 포인터는 포인터와 DST를 “완전하게”만드는 정보 (예 : 길이)를 포함합니다.

녹에서 가장 일반적으로 사용되는 유형은 하지 DSTS하지만, 컴파일 타임에 고정 된 크기를 가지고있다. 이러한 유형 Sized특성을 구현 합니다 . (같은 동적 크기의 힙 버퍼를 관리에도 종류가 Vec<T>) 있습니다 Sized컴파일러가 바이트의 정확한 수를 알고로 Vec<T>인스턴스가 스택에 소요됩니다. 현재 Rust에는 4 가지 종류의 DST가 있습니다.

슬라이스 ( [T]str)

유형 [T](모든 T)은 동적으로 크기가 조정됩니다 (특수한 “문자열 슬라이스”유형도 그렇습니다 str). 그렇기 때문에 일반적으로 참조 뒤에 &[T]또는 로만 표시됩니다 &mut [T]. 이 참조는 소위 “팻 포인터”입니다. 점검 해보자:

dbg!(size_of::<&u32>());
dbg!(size_of::<&[u32; 2]>());
dbg!(size_of::<&[u32]>());

이것은 (일부 정리와 함께) 인쇄합니다.

size_of::<&u32>()      = 8
size_of::<&[u32; 2]>() = 8
size_of::<&[u32]>()    = 16

따라서 일반적인 유형 u32에 대한 참조는 배열에 대한 참조 와 마찬가지로 8 바이트 크기 [u32; 2]입니다. 이 두 가지 유형은 DST가 아닙니다. 그러나 [u32]DST와 마찬가지로 이에 대한 참조는 두 배입니다. 슬라이스의 경우 DST를 “완료”하는 추가 데이터는 단순히 길이입니다. 따라서의 표현 &[u32]은 다음과 같다고 말할 수 있습니다 .

struct SliceRef {
    ptr: *const u32,
    len: usize,
}

특성 개체 ( dyn Trait)

특성 개체로 특성을 사용할 때 (즉, 유형이 지워지고 동적으로 전달됨) 이러한 특성 개체는 DST입니다. 예:

trait Animal {
    fn speak(&self);
}

struct Cat;
impl Animal for Cat {
    fn speak(&self) {
        println!("meow");
    }
}

dbg!(size_of::<&Cat>());
dbg!(size_of::<&dyn Animal>());

이것은 (일부 정리와 함께) 인쇄합니다.

size_of::<&Cat>()        = 8
size_of::<&dyn Animal>() = 16

다시 말하지만 일반 유형 &Cat이므로 크기가 8 바이트에 불과 Cat합니다. 그러나 dyn Animal특성 개체이므로 동적으로 크기가 조정됩니다. 따라서 &dyn Animal크기는 16 바이트입니다.

특성 개체의 경우 DST를 완료하는 추가 데이터는 vtable (vptr)에 대한 포인터입니다. 여기서 vtable과 vptr의 개념을 완전히 설명 할 수는 없지만이 가상 디스패치 컨텍스트에서 올바른 메서드 구현을 호출하는 데 사용됩니다. vtable은 기본적으로 각 메서드에 대한 함수 포인터 만 포함하는 정적 데이터 조각입니다. 이를 통해 특성 객체에 대한 참조는 기본적으로 다음과 같이 표현됩니다.

struct TraitObjectRef {
    data_ptr: *const (),
    vptr: *const (),
}

(이것은 추상 클래스에 대한 vptr이 객체 내에 저장되는 C ++와 다릅니다. 두 접근 방식 모두 장점과 단점이 있습니다.)

커스텀 DST

실제로 마지막 필드가 DST 인 구조체를 사용하여 자체 DST를 만들 수 있습니다. 그러나 이것은 다소 드문 경우입니다. 대표적인 예가 std::path::Path.

사용자 지정 DST에 대한 참조 또는 포인터도 팻 포인터입니다. 추가 데이터는 구조체 내부의 DST 종류에 따라 다릅니다.

예외 : Extern 유형

에서 RFC 1861extern type기능이 도입되었다. Extern 유형도 DST이지만 이에 대한 포인터는 팻 포인터 가 아닙니다 . 또는 더 정확하게는 RFC가 말한대로 :

Rust에서 DST에 대한 포인터는 가리키는 객체에 대한 메타 데이터를 전달합니다. 문자열과 슬라이스의 경우 이것은 버퍼의 길이이고, 특성 객체의 경우 이것은 객체의 vtable입니다. extern 유형의 경우 메타 데이터는 단순히 (). 이것은 extern 유형에 대한 포인터가 a와 크기가 동일하다는 것을 의미합니다 usize(즉, “팻 포인터”가 아님).

그러나 C 인터페이스와 상호 작용하지 않는 경우 이러한 extern 유형을 다룰 필요가 없을 것입니다.


위에서 우리는 불변 참조의 크기를 보았습니다. 팻 포인터는 변경 가능한 참조, 변경 불가능한 원시 포인터 및 변경 가능한 원시 포인터에 대해 동일하게 작동합니다.

size_of::<&[u32]>()       = 16
size_of::<&mut [u32]>()   = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>()   = 16


답변