enum의 원소들은 C의 union처럼 동작할 수 있도록 한 개 이상의 자료형을 가질 수 있다.
enum이 match를 통해 패턴 일치될 때, 각각의 데이터 값에 변수명을 붙일 수 있다.
enum의 메모리 상세
Ex)
#![allow(dead_code)] // 이 줄은 컴파일러 경고를 방지해줌
enum Species {
Crab,
Octopus,
Fish,
Clam,
}
enum PoisonType {
Acidic,
Painful,
Lethal,
}
enum Size {
Big,
Small,
}
enum Weapon {
Claw(i32, Size),
Poison(PoisonType),
None,
}
struct SeaCreature {
species: Species,
name: String,
arms: i32,
legs: i32,
weapon: Weapon,
}
fn main() {
// SeaCreature의 데이터는 stack에 있음
let ferris = SeaCreature {
// String struct도 stack에 있지만,
// heap에 있는 데이터에 대한 참조를 갖고 있음
species: Species::Crab,
name: String::from("Ferris"),
arms: 2,
legs: 4,
weapon: Weapon::Claw(2, Size::Small),
};
match ferris.species {
Species::Crab => match ferris.weapon {
Weapon::Claw(num_claws, size) => {
let size_description = match size {
Size::Big => "큰",
Size::Small => "작은",
};
println!(
"ferris는 {}개의 {} 집게를 가진 게이다",
num_claws, size_description
)
}
_ => println!("ferris는 다른 무기를 가진 게이다"),
},
_ => println!("ferris는 다른 동물이다"),
}
}
Option은 컴파일 타임에 크기를 결정해야 한다.
컴파일러는 Option의 크기는 T의 타입에 따라 가변적오르 메모리 할당이 발생한다.
use std::mem::size_of;
use std::mem::size_of_val;
// enum Option<T> {
// Some(T),
// None,
// }
struct Node32 {
data1: i32,
}
struct Node64 {
data1: i32,
data2: i32,
}
struct NodeOpt32 {
data1: Option<i32>,
}
struct NodeOpt64 {
data1: Option<i32>,
data2: Option<i32>,
}
fn main() {
println!("{}", size_of::<Node32>());//4
println!("{}", size_of::<Node64>());//8
println!("{}", size_of::<NodeOpt32>());//8
println!("{}", size_of::<NodeOpt64>());//16
let a = Node32 {
data1: 0
};
println!("{}", size_of_val(&a));//4
let b = Some(1_i32);
println!("{}", size_of_val(&b));//8
let c = Some(1_i64);
println!("{}", size_of_val(&c));//16
let d = Some(String::from("aaaaaaaaaaaaaaa"));
println!("{}", size_of_val(&d));//24
}
제네릭을 사용한다면 rust 컴파일러가 컴파일 타임에 메모리 크기를 결정 할 수 있도록 주의해야 한다.
use std::mem::size_of;
use std::mem::size_of_val;
struct Node<T> {
data1: T,
}
fn main() {
// 컴파일 타임에 결정 가능하여 문제 없음
println!("{}", size_of::<Node<i32>>());//4
println!("{}", size_of::<Node<i64>>());//8
let a = Node::<i32> {
data1: 0
};
println!("{}", size_of_val(&a));//4
let b = Node::<i64> {
data1: 0
};
println!("{}", size_of_val(&b));//8
}
재귀적인 타입 정의 에러는 C++ 뿐만 아니라 Rust에서도 문제 발생 할 수 있다.
// error 발생, Node<T> 타입 구조 결정하기 위해서
// Node<T> 안에 data2("Node<T>") 멤버 타입 크기를 결정해야 한다.
struct Node<T> {
data1: T,
data2: Node<T>, //error
}
fn main() {
println!("{}", size_of::<Node<i32>>());
println!("{}", size_of::<Node<i64>>());
}
9 | struct Node<T> {
| ^^^^^^^^^^^^^^ recursive type has infinite size
10 | data1: T,
11 | data2: Node<T>,
| ------- recursive without indirection
Option은 해결책이 되지 못한다. Option 역시 컴파일 타임에 크기를 결정한다.
struct Node<T> {
data1: T,
data2: Option<Node<T>>,//error
}
문제 해결을 위해서는 Box를 사용하여 Heap 메모리를 참조하도록 변경해야 한다.
use std::mem::size_of;
use std::mem::size_of_val;
struct Node<T> {
data1: T,
data2: Option<Box<Node<T>>>,
}
fn main() {
println!("{}", size_of::<Node<i32>>());//16
println!("{}", size_of::<Node<i64>>());//16
}
Option은 stack 메모리에 위치
pub fn add(num: i128) -> Option<i128> {
let a = Some(1948 + num);
a
}
example::add:
sub rsp, 40 <-- 스택 메모리 할당
mov rcx, rsi
mov rdx, rdi
add rdx, 1948
mov qword ptr [rsp], rdx
adc rcx, 0
seto al
mov qword ptr [rsp + 8], rcx
test al, 1
jne .LBB0_2
mov rax, qword ptr [rsp + 8]
mov rcx, qword ptr [rsp]
mov qword ptr [rsp + 24], rcx
mov qword ptr [rsp + 32], rax
mov qword ptr [rsp + 16], 1
mov rax, qword ptr [rsp + 16]
mov rdx, qword ptr [rsp + 24]
mov rcx, qword ptr [rsp + 32]
add rsp, 40
ret
.LBB0_2:
lea rdi, [rip + str.0]
lea rdx, [rip + .L__unnamed_1]
mov rax, qword ptr [rip + core::panicking::panic@GOTPCREL]
mov esi, 28
call rax
ud2
Box처럼 힙 메모리 할당은 보이지 않음
pub fn add(num: i128) -> Box<i128> {
let a = Box::new(1948 + num);
a
}
example::add:
sub rsp, 40
mov rcx, rsi
mov rdx, rdi
add rdx, 1948
mov qword ptr [rsp + 8], rdx
adc rcx, 0
seto al
mov qword ptr [rsp + 16], rcx
test al, 1
jne .LBB5_4
mov edi, 16
mov esi, 8
call alloc::alloc::exchange_malloc <--- 힙 메모리 할당
mov qword ptr [rsp], rax
jmp .LBB5_3
mov rcx, rax
mov eax, edx
mov qword ptr [rsp + 24], rcx
mov dword ptr [rsp + 32], eax
mov rdi, qword ptr [rsp + 24]
call _Unwind_Resume@PLT
ud2
.LBB5_3:
mov rax, qword ptr [rsp]
mov rcx, qword ptr [rsp + 16]
mov rdx, qword ptr [rsp + 8]
mov qword ptr [rax], rdx
mov qword ptr [rax + 8], rcx
jmp .LBB5_5
Result 역시 Option과 같은 enum이기에 Option과 같은 제약을 가진다.