일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- grafana
- transit gateway
- optional chaining
- 구조분해 할당
- Custom Resource
- Proxy Resource
- 자바스크립트
- Kubernetes
- 러스트
- JavaScript
- Endpoints
- docker
- prometheus
- 단축 평가
- null 병합 연산자
- cognito
- VPC
- rust
- Service
- elasticsearch
- AWS
- docker swarm
- Await
- api gateway
- Site-to-Site VPN
- 옵셔널 체이닝
- On-Premise
- 온프레미스
- 비구조화 할당
- vgw
- Today
- Total
만자의 개발일지
[Rust] 열거형 본문
저번 포스팅에서는 구조체와 메소드 & 연관 함수에 대해 알아보았습니다. 이번 포스팅에서는 또 다른 커스텀 타입 중 하나인 열거형과 패턴 매칭에 대해 알아보도록 하겠습니다.
열거형(Enums)
열거형은 다양한 형태의 값(variant)를 하나로 묶어주는 타입입니다.
Rust에서의 열거형은 단순한 고정된 값 뿐만 아니라 튜플, 구조체 타입의 값들도 가질 수 있습니다.
열거형은 enum 키워드를 사용해 정의할 수 있습니다. 다음 예시를 보겠습니다.
enum Message {
Quit, // unit-like
Move { x: i32, y: i32 }, // struct-like
Write(String), // tuple-like
ChangeColor(u8, u8, u8), // tuple-like
}
Message는 다양한 형태의 variant를 갖는 열거형입니다. 위처럼 열거형은 3가지 형태의 variant를 가질 수 있습니다.
- Quit 와 같이, 연관된 값(associated values)이 없는 variant입니다. unit-like variant 라고 합니다.
- Move 와 같이, 구조체처럼 필드를 갖는 variant입니다. struct-like variant 라고 합니다.
- Write, ChangeColor와 같이 여러 타입을 갖는 variant입니다. tuple-like variant 라고 합니다.
열거형의 각 variant들은 선언될 때 네임스페이스가 생성되어, 이중 콜론(::)을 사용하여 접근해야합니다.
각 variant는 다음과 같은 방식으로 인스턴스를 생성할 수 있습니다.
#[derive(Debug)]
enum Message {
Quit, // unit-like
Move { x: i32, y: i32 }, // struct-like
Write(String), // tuple-like
ChangeColor(u8, u8, u8), // tuple-like
}
fn main() {
let msg = Message::Quit; // path expression of the const item.
println!("{:?}", msg);
let msg = Message::Quit{}; // struct expression on the const item.
println!("{:?}", msg);
let msg = Message::ChangeColor(255, 255, 255); // call expression on the tuple item.
println!("{:?}", msg);
let msg = Message::ChangeColor{ 0: 255, 1: 255, 2:255 }; // struct expression on the tuple item.
println!("{:?}", msg);
let msg = Message::Move{ x: 100, y: 200 }; // struct expression on the struct item.
println!("{:?}", msg);
}
- unit-like variant - 경로 표현식(path expresssion) 또는 구조체 표현식(struct expression)을 통해 인스턴스를 생성할 수 있습니다.
- tuple-like variant - 호출 표현식(call expression) 또는 구조체 표현식을 통해 인스턴스를 생성할 수 있습니다.
- struct-like variant - 구조체 표현식을 통해 인스턴스를 생성할 수 있습니다.
열거형도 구조체와 마찬가지로 메서드와 연관 함수를 정의할 수 있습니다. impl 키워드를 사용하여 정의합니다.
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
impl Message {
fn new_write(s: String) -> Self { // 연관 함수
Message::Write(s)
}
fn print(&self) { // 메서드
match self {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(s) => println!("Write: {}", s),
Message::ChangeColor(r, g, b) => println!("Change color to: {}, {}, {}", r, g, b)
}
}
}
fn main() {
let msg = Message::new_write(String::from("Hello, Rust"));
msg.print(); // Write: Hello, Rust
}
print 메서드에 사용된 match 키워드는 아직 다루지 않은 내용입니다. 이에 대해서는 다음 포스팅에서 다루도록 하겠습니다.
Discriminants
Rust에서 열거형은 Tagged Union 방식으로 동작합니다.
Tagged Union이란 variant에 tag 값을 함께 저장하여 현재 어떤 vraiant인지를 나타내는 방식입니다.
Tagged Union은 잘못된 타입 접근을 방지하여 메모리 안전성을 높여줍니다.
C 언어에서는 tag 값에 따라 variant를 지정하는 코드를 별도로 작성해야 하지만, Rust 컴파일러는 이 과정을 자동으로 실행합니다.
Rust의 열거형에서 discriminant는 variant를 식별하는 tag 값입니다. 다음 예시를 보겠습니다.
enum Enum {
Foo,
Bar,
Baz
}
fn main() {
println!("{}", Enum::Foo as isize); // 0
println!("{}", Enum::Bar as isize); // 1
println!("{}", Enum::Baz as isize); // 2
}
discriminant는 isize타입의 값이며, 0부터 순차적으로 할당됩니다.
discriminant는 = 연산자로 지정할 수 있습니다. 그다음 생성되는 variant의 discriminant 값은 이전 variant의 다음 값이 할당됩니다.
enum Foo {
Bar, // 0
Baz = 123, // 123
Quux, // 124
}
#[repr(T)] 속성(attribute)을 사용해 discriminant의 타입을 제한할 수 있습니다.
#[repr(u8)]
enum Status {
Ready = 10,
Busy, // 11
Done, // 12
}
fn is_ready(status: Status) -> bool {
status as u8 == 10
}
fn main() {
let status = Status::Ready;
if(is_ready(status)) {
println!("Status is Ready");
}
}
discriminant의 타입을 u8로 제한하여 열거형의 크기를 줄일 수 있고, 이를 캐스팅하여 로직을 처리할 수 있습니다.
discriminant 를 사용할 때 다음과 같은 주의점이 있습니다.
enum SharedDiscriminantError {
SharedA = 1,
SharedB = 1
}
enum SharedDiscriminantError2 {
Zero, // 0
One, // 1
OneToo = 1 // 1 (collision with previous!)
}
#[repr(u8)]
enum OverflowingDiscriminantError {
Max = 255,
MaxPlusOne // 256, overflows
}
#[repr(u8)]
enum OverflowingDiscriminantError2 {
MaxMinusOne = 254, // 254
Max, // 255
MaxPlusOne // 256, overflows
}
- variant들은 같은 discriminant 값을 가질 수 없습니다.
- 타입의 최댓값을 넘어가는 경우 overflow가 발생합니다.
Zero-variant enums
어떠한 variant도 가지지 않는 열거형도 생성할 수 있습니다. 이를 zero-variant enum 라고 부르며, never type(!)과 동등하지만 never type과 달리 다른 타입으로 강제 변환(coerce)될 수 없습니다.
enum ZeroVariants {}
fn main() {
let x: ZeroVariants = panic!();
let y: u32 = x; // mismatched type error
}
Never type(!)
절대 값이 존재할 수 없는 타입입니다.
보통 절대로 반환되지 않는 함수의 반환타입 또는 특정 코드가 절대 실행되지 않음(unreachable)을 표현할 때 사용합니다.
함수가 실패하여 값을 반환하지 않는 경우
fn never_returns() -> ! {
panic!("Something went wrong!");
}
특정 코드가 실행되지 않음을 표현하는 경우
fn handle(x: Result<i32, !>) {
match x {
Ok(n) => println!("Got value: {}", n),
// Err(_) 실행되지 않음
}
}
Option<T>
Rust에는 null이 없습니다. 대신 Option<T>라는 열거형을 제공합니다.
Option<T>는 어떤 값의 존재여부를 표현하기 위한 열거형으로, Rust는 Option<T>를 사용하여 컴파일 타임에 null 처리를 강제함으로써 null 로 인한 런타임 오류(null pointer dereference)를 예방합니다.
Option<T>의 <T>는 제네릭 문법으로 인스턴스 내부에서 사용되는 데이터 타입을 외부에서 지정하는 문법입니다. 이에 대한 자세한 내용은 후에 다루도록 하겠습니다.
Option<T>는 다음과 같은 variant를 가집니다.
enum Option<T> {
None,
Some(T),
}
- None - 값이 없음을 나타내는 variant입니다.
- Some(T) - T 타입의 값이 존재함을 나타내는 variant입니다.
Option<T>는 Some에 어떠한 타입의 값이든 담을 수 있습니다. 아래는 Option<T> 사용 예시입니다.
fn main() {
let some_number = Some(1);
let some_char = Some('a');
let x: Option<u8> = None;
}
컴파일러는 Some 내부에 명시된 값의 타입을 자동으로 추론합니다. 하지만 None 값만으로는 Some이 어떤 타입의 값을 가질지 추론할 수 없기 때문에, 이 경우에는 타입을 명시해야합니다.
아래 코드를 실행하면 다음과 같은 에러가 발생합니다.
fn main() {
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
}
Option<T>와 T는 다른 타입이기 때문에 더하기 연산을 수행할 수 없습니다.
연산을 수행하기 전에 Option<T>를 T로 변환해야 합니다. 이 변환하는 과정을 강제함으로써 Rust는 null로부터 안전할 수 있습니다.
Option<T>를 T로 변환하는 과정은 다음 포스팅에서 다루도록 하겠습니다.
이번 포스팅에서는 열거형에 대해 알아보았습니다. 다음 포스팅에서는 패턴 매칭에 대해 알아보도록 하겠습니다.
참고
'Rust' 카테고리의 다른 글
[Rust] 구조체와 메서드 & 연관 함수 (0) | 2025.05.04 |
---|---|
[Rust] 조건문과 반복문 (0) | 2025.05.02 |
[Rust] 함수와 표현식 (0) | 2025.04.29 |
[Rust] 변수와 타입 (0) | 2025.04.28 |