구조체와 열거형의 복잡한 패턴 매칭과 가변 바인딩

구조체(Struct)와 열거형(Enum)의 여러가지 패턴 매칭(Match) 방법과 가변 바인딩(mutable binding)에 대해 알아보겠습니다. 구조체 분해후 바인딩, if let, _, .., | 등의 사용법과 참조와 Shadowing에 대해서도 알아봅니다.

1. 구조체(Struct)에서 복잡한 패턴 매칭과 가변 바인딩

가. 구조체 필드 일부만 매칭하기:

구조체 패턴에서 모든 필드를 반드시 매칭하지 않아도 되며, ..을 써서 나머지 필드는 무시할 수 있습니다.

struct Point {
x: i32,
y: i32,
z: i32,
}

fn main() {
let p = Point { x: 1, y: 2, z: 3 };

match p {
Point { x, .. } => println!("x 값에만 관심 있음: {}", x),
}
}

나. 가변 바인딩 (mutable binding)

기본적으로 Rust의 match에서 바인딩된 변수는 불변입니다. 가변으로 만들려면 mut 키워드를 변수 바인딩 전에 붙입니다.

let mut p = Point { x: 1, y: 2, z: 3 };

match &mut p { // 구조체를 가변 참조로 매칭
Point { x, y, .. } => {
*x += 10; // 가변 참조로 접근 가능
*y += 20;
println!("변경된 x: {}, y: {}", x, y);
}
}
  • 위 예에서 &mut p로 가변 참조를 패턴 매칭했고, 패턴 안의 x와 y는 가변 참조(&mut i32)가 되어 값을 변경할 수 있습니다.

다. 구조체 분해 후 변수 재바인딩

let p = Point { x: 10, y: 20, z: 30 };
let Point { x: a, y: b, z: c } = p;
println!("a: {}, b: {}, c: {}", a, b, c);

a, b, c가 각각 p의필드 값에 바인딩됩니다.

2. 열거형(Enum)에서 복잡한 패턴 매칭과 가변 바인딩

가. 열거형 variant에 포함된 여러 필드 매칭

enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}

fn main() {
let mut msg = Message::Move { x: 10, y: 20 };

match &mut msg {
Message::Move { x, y } => {
*x += 5; // 가변 참조로 내부 값 변경 가능
*y += 5;
println!("이동 좌표 변경: x={}, y={}", x, y);
}
_ => {}
}
}
  • match &mut msg를 통해 열거형 값을 가변 참조로 매칭하면, 내부 필드도 가변 참조로 바인딩되어 값 변경이 가능합니다.

나. 특정 variant만 언패킹하기

if let Message::Write(text) = msg {
println!("메시지: {}", text);
}
  • if let 문법을 활용해 관심 있는 variant 한 가지만 매칭해 변수 바인딩 후 처리할 수 있습니다.

다. 여러 variant를 한 패턴에 묶기

match msg {
Message::Quit | Message::Write(_) => println!("Quit 또는 Write variant"),
_ => println!("다른 variant"),
}
  • 여러 variant를 하나로 합쳐 처리할 수도 있습니다.

3. 패턴 바인딩 시 참조와 가변성

  • 기본적으로 match 패턴 내 변수 바인딩은 값 소유권을 가져오거나 복사하지만, 참조 및 가변 참조로도 바인딩할 수 있습니다.
let p = Point { x: 10, y: 20, z: 0 };

match &p { // 불변 참조 패턴 매칭
Point { x, y, .. } => println!("x={}, y={}", x, y),
}

let mut p2 = Point { x: 0, y: 0, z: 0 };

match &mut p2 { // 가변 참조 패턴 매칭
Point { x, y, .. } => {
*x += 1;
*y += 1;
println!("수정된 값 x={}, y={}", x, y);
}
}
  • 참조에 따라 변수 바인딩 시 가변성 여부가 달라지고, 이를 통해 구조체 또는 열거형 내부 값을 안전하게 변경할 수 있습니다.

4. 변수 이름 재사용(shadowing)과 패턴 매칭

패턴 매칭 시, 이전에 바인딩된 변수 이름과 동일한 이름을 써도 새로운 바인딩이 생성됩니다.

let x = 5;
let p = Point { x: 10, y: 20, z: 30 };

match p {
Point { x, y, .. } => {
// 여기 x는 패턴 내의 새 바인딩, 밖의 x와 다른 변수
println!("패턴 매칭 내의 x: {}, 외부 x: {}", x, 5);
}
}

5. 요약 정리

기능예시 (일부만)설명
구조체 부분 패턴 매칭Point { x, .. }특정 필드만 분해, 나머지는 무시
가변 참조 매칭match &mut p와 Point { x, y, .. }구조체 필드를 가변 참조로 바인딩해 값 수정 가능
열거형 가변 매칭match &mut msg와 Message::Move { x, y }enum 내부 필드를 가변 참조로 바인딩해 값 수정 가능
if let 구문if let Message::Write(text) = msg특정 variant만 골라 간단히 바인딩 후 처리
패턴 내 변수 이름 재사용Point { x, y } 내 x는새로운 바인딩기존 변수와 이름이 동일해도 새 변수로 처리

이상으로 구조체와 열거형에서 변수 바인딩 시 사용하는 다양한 패턴 매칭 기법과 가변 바인딩 방법에 대해 설명드렸습니다.

Rust에서 사용하는 기호와 연산자

Rust의 기호(=>, ::, ., -> 등)와 연산자(산술, 비교, 사칙연산자, 논리 연산자, 패턴 매치 연산자 등) 는 종류도 많고 다른 언어와 다른 것도 있어 많이 헷갈리므로 이것에 대해 전체적으로 알아보겠습니다.

Ⅰ. 기호

1. =>

match 문에서 패턴 매핑 결과를 지정할 때 사용합니다.

let num = 2;
match num {
1 => println!("one"),
2 => println!("two"),
_ => println!("other"),
}

위에서 2 => println!(“two”)는 num이 2일 때 실행됩니다.

2. ::

경로(네임스페이스) 구분자. 모듈, 구조체, 열거형, 연관함수, 상수 등에 접근할 때 사용합니다.

let color = Color::Red;
let s = String::from("hello");

Color 열거형의 Red variant, String 타입의 from 연관 함수에 접근합니다.

3. .

필드 접근하거나 메서드를 호출할 때 사용합니다.

let s = String::from("hi");
let len = s.len(); // 메서드 호출
let point = (3, 4);
let x = point.0; // 튜플의 첫 번째 요소

s.len()은 s 객체의 len 메서드를, point.0은 튜플의 첫번째 값을 뜻합니다.

4. ->

함수 또는 클로저의 반환 타입을 지정할 때 사용합니다.

fn add_one(x: i32) -> i32 {
x + 1
}

이 함수는 매개변수 x를 받아 i32 타입으로 결과를 반환합니다.

5. :

변수의 타입을 명시하거나 패턴 매칭에서 사용됩니다.

let score: i32 = 100;

변수 score의 타입이 i32임을 명시합니다.

아래는 패턴 매칭에서 :이 사용된 예입니다.
Point { x, y: 0 }에서 y: 0은 y 필드가 정확히 0일 때 매칭됨을 의미합니다.

struct Point { x: i32, y: i32 }

fn main() {
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: 0 } => println!("On the x axis at {x}"),
Point { x: 0, y } => println!("On the y axis at {y}"),
Point { x, y } => println!("On neither axis: ({x}, {y})"),
}
}

6. ;

구문(문장)의 끝을 표시합니다.

let x = 5;
println!("{}", x);

7. ,

목록(튜플, 인수 등)을 구분할 때 사용합니다.

rustlet point = (3, 4);
fn add(x: i32, y: i32) -> i32 { x + y }

8. ..

범위를 표현할 때 사용합니다.

for i in 1..5 {
println!("{}", i); // 1, 2, 3, 4 출력 (5는 포함X)
}

..는 시작값 이상, 끝값 미만의 범위를 의미하며, 끝값을 포함할 때는 ..=으로 사용합니다.

9. &

참조(Reference)를 의미합니다.

let x = 3;
let y = &x;

y는 x의 참조를 가집니다 (메모리 주소 공유).

10. *

역참조(Dereference)를 의미합니다.

let x = 5;
let y = &x;
println!("{}", *y); // y가 참조하는 실제 값(x) 출력

*y는 y가 가리키는 값을 가져옵니다.

11. @

의미: 패턴 매칭에서 값 바인딩

let v = Some(10);
if let id @ Some(x) = v {
println!("id: {:?}", id);
}

id @ Some(x)는 Some(10) 전체를 id에 바인딩합니다.

Ⅱ. 연산자

1. 산술 연산자

연산자설명예시결과 설명
+덧셈let a = 10 + 5;a는 15
뺄셈let b = 10 – 5;b는 5
*곱셈let c = 10 * 5;c는 50
/나눗셈let d = 10 / 2d는 5
%나머지let e = 10 % 3e는 1

2. 비교(관계) 연산자

연산자설명예시결과
==같다a == btrue 또는 false
!=같지 않다a != btrue 또는 false
>크다a > btrue 또는 false
<작다a < btrue 또는 false
>=크거나 같다a >= btrue 또는 false
<=작거나 같다a <= btrue 또는 false

3. 논리 연산자

연산자설명예시결과
&&논리 AND(a > 1 ) && (b < 5)둘 다 true면 true
<code>||</code>논리 OR`(a == 1)
!논리 NOT!is_validtrue->false, false->true

4. 비트 연산자

연산자설명예시결과
&비트 ANDa & b각 비트 AND
<code>|</code>비트 OR`ab`
^비트 XORa ^ b각 비트 XOR
!비트 NOT!a각 비트 반전
<<왼쪽 시프트a << 2비트를 왼쪽 이동
>>오른쪽 시프트a >> 1비트를 오른쪽 이동

5. 복합 할당 연산자

연산자설명예시
+=더해서 할당a +=1;
-=빼서 할당b -=2;
*=곱해서 할당c *= 3;
/=나눠서 할당d /= 2;
%=나머지를 할당e %= 4;

※ Rust는 ++와 –(증가/감소 연산자)를 지원하지 않습니다.

6. 기타 연산자

가. as: 타입 변환

let x: f32 = 10 as f32;

나. 단항 부정 연산자

-a : a의 음수
!a : a의 반전

Ⅲ. 패턴 매칭 관련 연산자 및 문법

match는 여러 패턴에 따라 코드를 분기할 수 있는 핵심 문법으로, C 계열의 switch보다 다양하고 강력한 매칭을 제공합니다.

let value = Some(7);
match value {
Some(x) if x > 5 => println!("greater than five: {}", x),
Some(x) => println!("got: {}", x),
None => println!("no value"),
}

1. | (or 패턴 연산자)

여러 패턴을 한 번에 처리할 수 있습니다.

let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("other"),
}
  • 이 예시에서 1 | 2는 x가 1 또는 2일 때 모두 해당 arm을 실행합니다.

2. _ (와일드카드/전부수용 패턴)

모든 값을 의미하며, 사용하지 않을 값을 무시할 때 씁니다.

match some_value {
1 => println!("one"),
_ => println!("other"),
}

3. @ (패턴 바인딩 연산자)

패턴과 동시에 값을 바인딩할 때 사용합니다.

let v = Some(42);
match v {
id @ Some(n) if n > 40 => println!("big! {:?}", id),
_ => println!("other"),
}
  • id @ Some(n)은 Some(42) 전체를 id에 바인딩하면서 n 값도 패턴 매칭합니다.

4. if let

특정 패턴만 처리하고 나머지는 무시하고 싶을 때 간결하게 쓸 수 있는 문법입니다.

if let Some(x) = option {
println!("have value: {}", x);
}

5. while let

while let은Rust에서 반복문과 패턴 매칭을 결합해, 어떤 값이 특정 패턴에 계속 일치하는 동안만 루프를 실행하는 구문입니다. 보통 Option, Result, Iterator 등에서 값을 꺼내거나 처리할 때 매우 자주 사용됩니다.

가. Stack처럼 값 꺼내기

let mut stack = vec![1, 2, 3];

while let Some(top) = stack.pop() {
println!("스택에서 꺼냄: {}", top);
}
  • 벡터에서 값을 꺼내는 동작이지만, 동시에 벡터를 스택(LIFO)처럼 쓰는 대표적인 패턴이라 “스택처럼 값 꺼내기”라고 표현한 것입니다.
  • stack.pop()이 벡터의 마지막 요소를 꺼내서(Some) 반환합니다. 벡터가 비어 있다면 None을 반환합니다. Vec은 동적 배열이지만, pop은 이를 스택처럼 사용하게 해 줍니다.
  • 값이 없을 때 None이 반환되고 루프가 끝납니다.

나. Option을 이용한 카운팅

let mut optional = Some(0);

while let Some(i) = optional {
if i > 9 {
println!("9보다 커서 종료!");
optional = None;
} else {
println!("현재 값: {}", i);
optional = Some(i + 1);
}
}
  • optional이 Some(i)에 매칭되는 동안 루프 실행.

다. Result 타입, Iterator 등에도 활용

let mut results = vec![Ok(1), Err("Error"), Ok(2)];

while let Some(res) = results.pop() {
match res {
Ok(v) => println!("ok: {}", v),
Err(e) => println!("err: {}", e),
}
}
  • Result값을 반복적으로 처리하다가 벡터가 비면 자동 종료.

라. while let, if let, match 비교

구문실용 상황특징와 차이점
while let값이 반복적으로 패턴에 매칭될 때패턴 매칭이 실패하면 자동으로 루프 종료
if let단일 조건만 한 번 검사할 때한 번만 검사, 반복X
match모든 경우의 수를 분기 처리할 때모든 possibility처리, 반복X

패턴 매칭과 제어 흐름 심화 (match, if let, while let)

Rust는 패턴 매칭을 통해 다양한 값 구조를 해체하고 조건에 따라 분기할 수 있도록 지원합니다. match, if let과 while let은 이를 위해 자주 사용하는 문법입니다.


1. 복습: match 표현식

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}
  • enum은 Coin의 종류를 열거하고 있습니다.
  • match는 모든 경우를 명시해야 합니다. 위 경우 Coin의 종류 4개를 모두 다루고 있습니다.
  • 컴파일러가 exhaustiveness(완전성 – 모든 경우를 다룸)을 검사합니다.
  • 함수에서 coin을 인수로 받는데, type은 Coin 열거형이고, 반환 type은 u8로 0과 양수입니다.
  • 열거형을 분기할 때 열거형의 이름에 variant(변형)을 ::으로 연결하며,
    반환 값은 => 다음에 적고, 각각의 경우를 ,로 구분합니다.

위 코드를 실행하려면 main 함수가 아래와 같이 필요합니다.

fn main() {
    let coin1 = Coin::Penny;
    println!("{} 센트", value_in_cents(coin1));
}

Coin의 종류를 Coin:: 다음에 입력하면 value_in_cents 함수에 의해 해당하는 센트를 표시합니다.

{}가 값이 출력될 위치(placeholder)이고, 값은 value_in_cents(coin1)으로 구합니다.


2. 패턴 바인딩

enum Message {
    Hello { id: i32 },
}

fn main() {
    let msg = Message::Hello { id: 42 };

    match msg {
        Message::Hello { id: 1 } => println!("ID는 1"),
        Message::Hello { id } => println!("다른 ID: {}", id),
    }
}
  • Message 열거형은 Hello 구조체를 변형으로 갖으므로 id에 해당하는 정수를 값으로 받습니다.
  • let msg = Message::Hello { id: 42 };
    : Message 열거형으로 Hello 구조체를 만드는데 id는 42입니다.
  • 이 패턴은 Hello { id: i32 }에 매칭되며, id 값을 꺼내서 변수 id에 바인딩합니다.
  • 따라서, 두번째 분기만 있어도 되는데, 1인 경우를 별도의 경우로 빼낸 것입니다.
    다시 말해, 특정 값 비교와 변수 추출을 동시에 할 수 있습니다.

3. 와일드카드와 _, |, .., ..=

fn main() {
    let x = 7;

    match x {
        1 => println!("하나"),
        2 | 3 => println!("둘 또는 셋"),
        4..=6 => println!("4에서 6 사이"),
        _ => println!("기타"),
    }
}
  • |: 여러 패턴 매칭(or).
    위 경우 2 | 3은 2또는 3인 경우가 됩니다.
  • ..=6: 범위 매칭(=은 포함, 없으면 미만).
    위 경우 4..=6은 4이상 6이하가 되며, 4..6은 4이상 6미만이므로 4와 5만 해당됩니다.
  • _: 나머지 모든 경우 (wildcard, else)
  • x의 데이터 형식은 정수이므로 i32가 돼서 부호있는 32비트 정수이므로 -2^31 (약 -21억)부터 2^31 – 1 (약 21억)까지의 값을 표현 가능
  • 위 코드를 실행하면 x가 7이므로 _에 해당되어 “기타”가 출력됩니다.

4. if let 표현식

match가 너무 장황할 때 if let을 사용해 간단히 표현할 수 있습니다.

let some_value = Some(5);

if let Some(x) = some_value {
    println!("값은 {}", x);
} else {
    println!("값이 없습니다");
}
  • Some은 Option enum의 variant중 하나로서, if let을 이용해서 match의 단일 분기를 간단히 표현한 것이며,
    match 연산자를 이용하면 아래와 같이 해야 됩니다.
    match some_value {
        Some(x) => println!("값은 {}", x),
        None => println!("값이 없습니다"),
    }
  • if let Some(x) = some_value는 “x가 5라면”이 되므로, “값은 5″가 출력됩니다.
  • match 연산자의 경우는 None을 사용하는데, if 제어문이라 else를 사용했는데 없어도 됩니다.

5. while let 반복문

let mut stack = vec![1, 2, 3];

while let Some(top) = stack.pop() {
    println!("꺼낸 값: {}", top);
}
  • vec은 아직 설명하지 않았는데, array가 크기가 고정되어 있다면 vec은 가변 길이의 배열입니다.
  • while let을 이용하면 값이 존재하는 동안 반복합니다.
  • Option, Result와 같이 반복 가능한 열거형에 유용
  • 위 코드를 main 함수에 넣고 실행하면 아래와 같이 출력됩니다.

🧠 요약

문법설명
match여러 경우를 완전하게 분기
바인딩패턴 내부에서 변수에 값 대입 가능
_기타 모든 경우 처리
if let한 가지 매칭에 적합한 축약형
while let값이 남아 있는 동안 반복

열거형 (Enums)

Rust는 복잡한 데이터와 다양한 상태를 안전하게 표현할 수 있는 강력한 도구인 열거형(enum)을 제공합니다.
enum은 단순한 값 목록이 아닌, 각 변형(variant)에 고유한 데이터를 담을 수 있어 패턴 매칭(match)과 결합해 매우 유용하게 사용됩니다.


1. 기본 열거형의 정의와 사용

열거형은 여러 개의 이름 있는 변형을 정의하는 타입입니다.
enum 다음에 이름을 입력하고 중괄호 안에 variant(변형)를 입력합니다.

enum Direction {
    North,
    South,
    East,
    West,
}

아래와 같이 함수에 열거형을 사용할 때는 함수명을 적고 인수를 입력하는데, 인수의 형식은 열거형이 됩니다. 그리고, match 흐름 제어 연산자를 사용하는데, match 다음에 인수명을 기재하고, 분기(arm)를 정의하는데 열거형의 이름 다음에 ::을 추가하며, =>을 사용해 실행 코드를 지정합니다.

fn move_to(dir: Direction) {
    match dir {
        Direction::North => println!("북쪽으로 이동"),
        Direction::South => println!("남쪽으로 이동"),
        Direction::East  => println!("동쪽으로 이동"),
        Direction::West  => println!("서쪽으로 이동"),
    }
}

main 함수는 아래와 같이 let을 이용해 direction 변수에 열거형 이름::변형을 입력하고, 위 함수 move_to의 인수로 direction 변수를 입력하면 “북쪽으로 이동”이란 글자가 화면에 표시됩니다.

fn main () {
    let direction = Direction::North;
    move_to(direction); 
}

North를 달리하면 출력 결과가 달라지며, 변형에 없는 값, 아래에서는 North2를 입력하면 “Direction에서 (North2) variant가 발견되지 않는다”라는 에러 메시지가 표시되므로 정확한 입력을 보장할 수 있습니다.


2. 열거형 변형에 데이터 저장

열거형은 각 변형마다 다른 타입의 데이터를 가질 수 있습니다. 이 점이 Rust enum의 강력한 특징입니다.

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

각 변형은 정해진 구조의 데이터를 저장할 수 있습니다:

  • Quit은 데이터 없음
  • Move는 구조체 형태(중괄호 사용)
  • Write는 문자열
  • ChangeColor는 튜플 형태(소괄호 사용)
fn process(msg: Message) {
    match msg {
        Message::Quit => println!("종료"),
        Message::Move { x, y } => println!("이동: ({}, {})", x, y),
        Message::Write(text) => println!("메시지: {}", text),
        Message::ChangeColor(r, g, b) => println!("색상 변경: {}, {}, {}", r, g, b),
    }
}

fn main() {
    let msg1 = Message::Quit;
    let msg2 = Message::Move { x: 10, y: 20 };
    let msg3 = Message::Write(String::from("안녕하세요"));
    let msg4 = Message::ChangeColor(255, 0, 0);

    process(msg1);
    process(msg2);
    process(msg3);
    process(msg4);
}

위 코드를 실행하면 아래와 같이 분기에 따라 실행 코드가 화면에 출력됩니다.

종료
이동: (10, 20)
메시지: 안녕하세요
색상 변경: 255, 0, 0

3. match 표현식

enum과 함께 가장 강력하게 사용되는 문법이 match입니다. 모든 경우를 exhaustively(빠짐없이) 처리하도록 강제되어, 안전한 분기 로직을 작성할 수 있습니다.

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(String),
}

fn main() {
    
    let coin = Coin::Quarter("New York".to_string());

    match coin {
        Coin::Penny => println!("1원!"),
        Coin::Nickel => println!("5원!"),
        Coin::Dime => println!("10원!"),
        Coin::Quarter(state) => println!("25원 from {:?}", state),
    }
}

  • enum Coin이 네 가지가 있으므로 match 연산자는 네 가지 분기를 모두 작성해야 합니다. 위 코드에서 match 분기의 하나를 주석 처리하고 실행하면

“모든 경우를 소진하지 않았다(none-exaustive patterns)”고 하면서 ‘Coin:Dime” not covered라고 “Dime 코인을 커버하지 않았다”고 합니다.

  • 모든 경우를 망라하기 어렵다면 _를 이용해 위를 제외한 다른 경우는 모두 이에 해당하는 실행 코드를 적용하도록 할 수 있습니다.
    match coin {
        Coin::Penny => println!("1원!"),
        Coin::Nickel => println!("5원!"),
        // Coin::Dime => println!("10원!"),
        // Coin::Quarter(state) => println!("25원 from {:?}", state),
        _ => println!("기타 동전!"),
    }

4. if let 표현식

단일 패턴만 확인하고 나머지는 무시하고 싶을 때는 if let 구문을 이용해 더 간결하게 처리할 수 있습니다.

    let coin = Coin::Penny;
    if let Coin::Penny = coin {
        println!("1원!");
    }

if 문의 내용이 “같다면”인데 let 문이므로 ==이 아니라 =를 사용했다는 것, 그리고 Coin::Penny가 앞에 왔다는 점을 주의해야 합니다. 위 코드를 실행하면 “1원!”가 화면에 출력됩니다.

if let coin = Coin::Penny 이라고 순서를 바꿔 표시하면 에러는 나지 않는데 coin 변수를 사용하지 않았으므로 _coin으로 변수명을 바꾸라는 제안을 합니다.

match보다 간단하지만, 나머지 경우는 무시되므로 사용에 주의가 필요합니다.


5. 열거형은 메서드도 가질 수 있다

열거형도 구조체처럼 impl 블록을 통해 메서드를 정의할 수 있습니다.

enum Status {
    Ready,
    Waiting,
    Error(i32),
}

impl Status {
    fn print(&self) {
        match self {
            Status::Ready => println!("준비 완료"),
            Status::Waiting => println!("대기 중"),
            Status::Error(code) => println!("에러 코드: {}", code),
        }
    }
}
fn main() {
    let status1 = Status::Ready;
    let status2 = Status::Waiting;
    let status3 = Status::Error(404);

    status1.print();
    status2.print();
    status3.print();
}

위 코드를 실행하면 아래와 같이 화면에 표시됩니다.

준비 완료
대기 중
에러 코드: 404

6. Option 열거형

Rust 표준 라이브러리에는 매우 자주 쓰이는 열거형인 Option이있습니다. 이는 값이 있을 수도 있고, 없을 수도 있다는 개념을 타입 시스템으로 안전하게 표현합니다.

enum Option<T> {
    Some(T),
    None,
}

예시:

let some_number = Some(5);
let no_number: Option<i32> = None;

이 방식은 null을 사용하지 않고도 안전하게 결측 값을 표현할 수 있게 해줍니다.

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        Some(n) => Some(n + 1),
        None => None,
    }
}

위 함수 plus_one은 인수 x가 정수(i32)라면 +1을 하고, None이라면 None을 반환하라는 의미인데, Option Enum이라 정수를 그냥 입력하면 안되고, Some 괄호 안에 입력해야 합니다.

fn main() {
    let num1 = Some(5);
    let num2 = None;

    println!("Result 1: {:?}", plus_one(num1)); // Result 1: Some(6)
    println!("Result 2: {:?}", plus_one(num2)); // Result 2: None
}

위와 같이 main 함수를 만들어 실행하면 숫자 5가 Some(5)라고 입력되면 Some(6)이 반환되고, None이 입력되면 None이 반환됩니다.

Rust는 기존의 사고 방식을 모두 바꿔버리니 적응하기 어렵습니다.


마무리

Rust의 열거형은 단순한 열거 상수를 넘어서 다양한 형태의 상태를 표현하는 강력한 수단입니다.특히 Option, Result, match와 결합하면 null, 에러, 상태 관리 등의 문제를 컴파일 타임에 안전하게 해결할 수 있습니다.