Rust의 vtable을 시각화하며 이해하는 dyn Trait의 메모리 구조

Visualizing Rust's Vtables: How dyn Trait Works In Memory

Rust의 vtable을 시각화하며 이해하는 dyn Trait의 메모리 구조

Rust를 배우는 저자가 C++과의 비교를 통해 Rust의 다형성, 특히 dyn Trait의 내부 동작을 탐구합니다. 정적 디스패치(제네릭)와 동적 디스패치(dyn Trait)의 차이를 설명하고, 와이드 포인터(데이터 포인터 + vtable 포인터)의 구조와 (타입, 트레이트) 쌍별로 vtable이 생성되는 방식을 메모리 주소를 직접 출력하며 시각화합니다. 또한 Rust의 제로 사이즈 타입(ZST)이 메모리를 차지하지 않는 이유와 C++과의 철학적 차이를 다룹니다.

Rust에서 디스패치 방식의 선택은 클래스 수준이 아닌 호출 지점에서 이루어집니다. Circle은 그저 Circle일 뿐이며, 디스패치에 대해 아무것도 알지 못합니다.
  1. tialaramex

    이 글에는 Object Safety에 대한 섹션이 있는데, 확인해 보니 이 글은 이번 주에 작성되었지만, "Object Safety"는 이 개념에 대해 혼란스러운 이름이라서, 잠시 동안 Rust에서는 이 개념을 "dyn compatibility"라고 부릅니다. 왜냐하면 트레잇이 "dyn compatible"일 때 얻을 수 있는 가장 중요한 것은 "dyn Trait"을 사용할 수 있다는 것이기 때문입니다 - https://doc.rust-lang.org/1.98.1/reference/items/traits.html...

    그 링크는 규칙을 더 포괄적으로 설명합니다.

    [편집: 글의 끝부분에서 이 내용을 설명한다는 것을 알게 되었습니다. 저자는 몇 달 전에 글을 쓰기 시작했는데, 아마도 개선된 "dyn compatibility" 명칭에 대해 읽기 전이었을 것입니다.]

  2. Panzerschrek

    > 트레잇 객체로 사용되려면 트레잇은 소위 객체 안전 규칙을 따라야 합니다.

    이것은 제가 보기에 Rust의 주요 설계 결함인 것 같습니다. Rust는 동적 다형성에 트레잇을 재사용하려고 하는데, 완벽하게 맞지 않음에도 불구하고요. C++은 더 정직합니다. 정적 다형성(템플릿)과 동적 다형성(상속)을 위한 두 가지 별도의 메커니즘이 있기 때문입니다.

  3. Panzerschrek

    저는 Rust에서 팻 포인터(가상 테이블을 포함하는)와 관련된 까다로운 버그를 겪은 적이 있습니다. 두 개의 그러한 포인터는 같은 객체를 가리키더라도 서로 다를 수 있습니다. 왜냐하면 (어떤 이유로) 컴파일러가 가상 함수 테이블의 두 개(또는 그 이상)의 복사본을 만들어 서로 다른 곳에서 사용할 수 있기 때문입니다.

  4. evmar

    저의 발견 과정에서 https://cheats.rs/ 가 매우 도움이 된다는 것을 알게 되었고, 특히 그 "memory layout" 섹션에는 시각화가 있습니다. (그 사이트와 아무 관련이 없으며, 그저 만족한 독자입니다!)

  5. returningfory2

    아주 좋습니다. 후속으로 vtable 자체의 구조를 리버스 엔지니어링하는 것도 흥미로울 것입니다. 아마도 메서드 구현에 대한 포인터 목록일 것 같습니다?

이 날의 다른 글

2026-09-05