Rust의 vtable을 시각화하며 이해하는 dyn Trait의 메모리 구조
Visualizing Rust's Vtables: How dyn Trait Works In Memory

Rust를 배우는 저자가 C++과의 비교를 통해 Rust의 다형성, 특히 dyn Trait의 내부 동작을 탐구합니다. 정적 디스패치(제네릭)와 동적 디스패치(dyn Trait)의 차이를 설명하고, 와이드 포인터(데이터 포인터 + vtable 포인터)의 구조와 (타입, 트레이트) 쌍별로 vtable이 생성되는 방식을 메모리 주소를 직접 출력하며 시각화합니다. 또한 Rust의 제로 사이즈 타입(ZST)이 메모리를 차지하지 않는 이유와 C++과의 철학적 차이를 다룹니다.
Rust에서 디스패치 방식의 선택은 클래스 수준이 아닌 호출 지점에서 이루어집니다. Circle은 그저 Circle일 뿐이며, 디스패치에 대해 아무것도 알지 못합니다.
HN 토론
30- 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" 명칭에 대해 읽기 전이었을 것입니다.]
- Panzerschrek
> 트레잇 객체로 사용되려면 트레잇은 소위 객체 안전 규칙을 따라야 합니다.
이것은 제가 보기에 Rust의 주요 설계 결함인 것 같습니다. Rust는 동적 다형성에 트레잇을 재사용하려고 하는데, 완벽하게 맞지 않음에도 불구하고요. C++은 더 정직합니다. 정적 다형성(템플릿)과 동적 다형성(상속)을 위한 두 가지 별도의 메커니즘이 있기 때문입니다.
- Panzerschrek
저는 Rust에서 팻 포인터(가상 테이블을 포함하는)와 관련된 까다로운 버그를 겪은 적이 있습니다. 두 개의 그러한 포인터는 같은 객체를 가리키더라도 서로 다를 수 있습니다. 왜냐하면 (어떤 이유로) 컴파일러가 가상 함수 테이블의 두 개(또는 그 이상)의 복사본을 만들어 서로 다른 곳에서 사용할 수 있기 때문입니다.
- evmar
저의 발견 과정에서 https://cheats.rs/ 가 매우 도움이 된다는 것을 알게 되었고, 특히 그 "memory layout" 섹션에는 시각화가 있습니다. (그 사이트와 아무 관련이 없으며, 그저 만족한 독자입니다!)
- returningfory2
아주 좋습니다. 후속으로 vtable 자체의 구조를 리버스 엔지니어링하는 것도 흥미로울 것입니다. 아마도 메서드 구현에 대한 포인터 목록일 것 같습니다?