Types
들어가면서
We’ll skip past the basics covered in The Rust Programming Language and instead dive headfirst into how different types are laid out in memory, the ins and outs of traits and trait bounds, existential types, and the rules for using types across crate boundaries.
기존에 스터디했던 Rust 책에서는 Type 에 대한 설명이라면 여기서는 Memory에 배치되는 방법, trait 경계, crate 경계에 대한 설명
Types in Memory
어떤 값은 Type을 지정할때까지 메모리 할당 측면에서 의미가 없다. 예를 들어 u8의 Type으로 187 이라고 표현되는 숫자는 i8에서는 -67이다.
Alignment
<aside>
💡 Memory에서 Type은 Byte 단위로 정렬이 되어야한다는 내용입니다.
</aside>
- Type이 정해지면 메모리의 임의의 위치에 할당될 수 있다는 의미가 된다.
- 포인터를 생각해보면 바이트 단위로 주소를 가르키고 있다는 것을 우리는 이미 알고 있습니다.
- 결국 1 byte 곧 8 bit 의 배수로 Alignment 된단고 생각하면 됩니다.
<aside>
💡 그러나 실제 CPU에서는 32 bit(4 byte) 혹은 64 bit(8 byte) 단위로 Alignment 된다고 생각하면 됩니다.
</aside>
- 32/64 bit CPU 라고 하면 32/64 bit 가 한 Word 단위라고 생각하면 된다.
<aside>
💡 하드웨어의 "Native" alignment 를 알아야한다. 뜻을 유추하기로 하드웨어에 맞는 word alignment를 가능하도록 해야한다
</aside>
- 예를 들어 Byte단위로 무조건 할당을 하면 i64 같은 8 byte단위의 type 이 word alignment가 안될 수 있다
- 위 상황이면 하드웨어는 2개의 word를 읽어야하는 문제가 발생한다
- 이런 상황은 작업 성능을 떨어뜨린다
- 오정렬(misaligned access) 는 word 단위가 아닌 단위로 정렬되는 것을 말한다
- 강제로 byte align을 한다면 예1)은 int 가 word 와 word 사이에 걸려서 성능 저하
- Native alignment 를 한다면 예1)은 3 word 의 공간이 필요함 예2)는 2 word로 됨
예를 들어 32 bit CPU 에서 아래와 같이 정의 한다고 하면(이해를 돕기위해 예제를 만들어봄)
예1) struct Test{ char aaa; // 8bit short bbb; // 16bit int ccc; // 32bit char ddd; // 8bit }; 예2) struct Test{ char aaa; // 8bit char ddd; // 8bit short bbb; // 16bit int ccc; // 32bit };
Layout
컴파일러가 Type에 대해서 메모리 내에 표현하는 방법을 Layout 이라고 함.
<aside>
💡 RUST 는 기본적으로 Type을 어떻게 배치하는지에 대한 Layout 을 보장하지 않음
</aside>
- repr(C) 를 사용하여 C/c++의 layout을 가져올 수 있다
- 위 C 예제와 유사한 아래 Rust 에서 repr을 사용한 예제
- tiny는 1bit지만 1 byte 할당
- normal 은 4byte 정렬을 하려고 하지만 할수 없음
- tiny 뒤에 3 byte 패딩 byte 필요
- small은 바로 align 가능
- long 은 small 이 1byte 를 사용하고 long 8 byte 정렬이 필요하기 때문에 small 뒤에 7byte 패딩
- 그뒤로 short은 2바이트 바로 할당
- 마지막으로 Struct Foo 는 가장근 항목인 long의 크기로 할당을 해야하므로 short 뒤에 6 byte 패딩 넣어서 총 32 byte 의 크기를 갖는다

<aside>
💡 repr(C) 를 사용하지 않고 Rust 의 기본 Struct 구조를 사용한다면 필드의 순서를 동일하게 맞출 필요가 없고 따라서 동일한 Struct 라도 Layout이 같다고 볼수 없다
</aside>
- Rust 는 필드의 순서를 컴파일러가 조정할 수 있음으로 위의 3-1의 예제를 크기가 작은 필드 순서대로 layout 하여 16 byte 로 만들고 코드를 더 효율적으로 생성 할 수 있다.
- 기본적으로 misaligned access 를 감수한다
- misaligned access 가 치명적인 환경에서는 #[repr(packed)] 를 사용하여 word 에 align 하도록 할 수 있다.
Complex Types
- Tuple
- 같은 type의 필드가 있는 구조체처럼 저장
- Array
- Item 사이에 패딩이 없이 연속적으로 저장
- Union
- Struct 와 유사하지만 각 필드가 같은 주소의 메모리를 사용하기 때문에 크기가 가장 큰 필드의 Type으로 결정됨
- Enumeration
- Union과 같은데 enum variant discriminant 를 저장하는 숨겨진 공간이 있고 이것에 따라서 크기가 결정됨
Dynamically Sized Types and Fat Pointers
- 컴파일러는 크기가 지정된 Type을 원한다.
- Rust 에서도 대부분의 Type은 크기를 자동으로 알수 있다
- Trait 와 Slice 는 Compile time 에 크기를 알수 없다
- Vector 와 같은 가변 형식에 Iterator 를 구현하는 trait 을 말하는 것
- Dynamically Sized Types(DST) 라고 칭함
- 잘 이해는 못하겠지만 크기가 큰 Pointer 로 해결함
- Fat pointer 는 word 의 2배만큼 잡는다.
- pointer 를 유지
- type을 완료하는 추가 정보
And crucially, that fat pointer is Sized. Specifically, it is twice the size of a usize (the size of a word on the target platform): one usize for holding the pointer, and one usize for holding the extra information needed to “complete” the type.
Traits and Trait Bounds
Compilation and Dispatch
- Generic T 로 함수를 구현하면 T의 Type 마다 구현된 모든 코드 블록을 복사본을 갖고 있어야 함(Compilation)
- CPU 는 Type 별로 따로 저장된 복사본의 method가 있는 주소를 각각 알아야하고 주소를 갖아야한다(Dispatch)
- 모든 주소를 다 알아야 하는 것은 Static Dispatch라고 한다.

- 개념은 간단하지만 Type 별로 method들을 복사본으로 가지고 있어야 해서 비효율적임
- 컴파일러는 실제로 전체 복사를 하는 것은 아니고 trait
In reality, the compiler does not actually do a full copy-paste. It copies only parts of the code that you use, so if you never call find on a Vec<i32>, the code for find won’t be copied and compiled.'
- 이 문제를 해결하는 방법은 Dynamic Dispatch 이다

Generic Traits
- Getneric type parameter
Foo<T>
- Associated type parameter
- Foo{ type Bar;}
- 표준 라이브러리에서 사용 예
pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; } impl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option<Self::Item> {
아래와 같이 Generic type으로 구현한다면 next method를 실행 할때 T에 대해서 매번 Type을 지정해줘야한다.
pub trait Iterator<T> { fn next(&mut self) -> Option<T>; }
<aside>
💡 Ignis 에서 list view 에서 camera/group struct 를 type으로 받아서 같이 처리하는 method 를 했던 기억이??
</aside>
Coherence and the Orphan Rule
- Coherence
- "일관성" 이란 컴파일러가 어떤 Crate에서 Trait 를 구현해야하는 지 알아야하는 상황
- 가장 간단한 방법은 trait 를 한 crate에서만 구현하면 충돌은 없지만 공유도 없어서 의미가 없다.
- 이 문제를 해결하는 것이 rust 에서 Orphan Rule
- Orphan Rule
- 로컬에서만 trait 을 구현 가능하다는 말은??
Simply stated, the orphan rule says that you can implement a trait for a type only if the trait or the type is local to your crate.
Blanket Implementations
impl<T> MyTrait for T where T
Fundamental Types
Covered Implementations
Trait Bounds
Generic Trait 를 구현 할 때 어떤 Type이던 구현 가능 한 것이 아니라 특정 Type을 선택하여 컴파일러에게 알려줌
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 { 또는 fn some_function<T, U>(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug {
Marker Traits
- 일반적인 trait 와 달리 Marker trait 은 type의 속성을 나타냄
- Send, Sync, Copy, Sized, Unpin 등이 포함된다.
For example, if a type implements the Send marker trait, it is safe to send across thread boundaries.
If it does not implement this marker trait, it isn’t safe to send.
Send marker trait 이 구현되지 않으면 Thread 간 통신에 쓰이는 Send가 안전하지 않다는 것인데 Type의 속성을 나타낸다는 것이 정확히..
Existential Types
- Rust 에서는 Function내에서 선언하는 변수 유형이나 호출하는 method 들에 대한 인자 유형을 지정을 거의 안한다.
- 컴파일러가 type을 유추하기 때문
- 변수/클로저와 method 인자 정도는 유추를 함
- Function type, trait 등은 Type을 지정해야함
For example, if you return a closure from a function, or an async block from a trait method, its type does not have a name that you can type into your code.
To handle situations like this, Rust supports existential types. Chances are, you have already seen existential types in action. All functions marked as async fn or with a return type of impl Trait have an existential return type: the signature does not give the true type of the return value, just a hint that the function returns some type that implements some set of traits that the caller can rely on. And crucially, the caller can only rely on the return type implementing those traits, and nothing else.
댓글