Rust 05. Structs, Enums, Pattern Matching, Traits 기초
요약
ownership, borrowing, lifetime까지 익혔다면 이제 “데이터를 어떤 형태로 모델링하고, 여러 경우를 어떻게 안전하게 분기하며, 공통 동작을 어떤 단위로 묶을 것인가”를 볼 차례다. Rust에서는 이 지점을 struct, enum, match/if let, trait가 맡는다.
이 글은 위 네 가지를 하나의 Cargo 프로젝트 흐름으로 연결해 정리한다. 결론부터 말하면 struct는 관련 데이터를 묶고, enum은 여러 상태 중 하나를 표현하고, match와 if let은 그 상태를 안전하게 꺼내며, trait는 서로 다른 타입에 같은 동작 계약을 부여한다.
문서 정보
- 작성일: 2026-04-12
- 검증 기준일: 2026-04-16
- 문서 성격: tutorial
- 테스트 환경: Windows 11 Pro, Cargo 프로젝트, Windows PowerShell 예시 명령,
src/main.rs - 테스트 버전: rustc 1.94.0, cargo 1.94.0
- 출처 등급: 공식 문서만 사용했다.
- 비고: 대표 예제를 로컬에서 재실행했고, 예제에 따라 unused variant 경고가 함께 보일 수 있다.
문제 정의
Rust 입문 단계에서 아래 네 가지는 서로 연결해서 보지 않으면 이해가 끊기기 쉽다.
- 여러 필드를 하나의 타입으로 어떻게 묶을지
- 여러 상태 중 하나를 타입 수준에서 어떻게 표현할지
- enum 값을 꺼낼 때 누락 없이 어떻게 분기할지
- 구조는 다르지만 같은 동작을 여러 타입에 어떻게 부여할지
이 글은 위 질문을 하나의 흐름으로 연결하는 데 초점을 둔다. derive macro 심화, generic trait bound 전체, trait object, 고급 pattern guard는 범위에서 제외한다.
확인된 사실
struct는 관련 데이터를 하나의 사용자 정의 타입으로 묶는 기본 도구다. 근거: Defining and Instantiating Structsimpl블록은 특정 타입과 밀접한 메서드를 같은 단위에 배치하는 방법이다. 근거: Method Syntaxenum은 여러 variant 중 정확히 하나를 표현하며, 각 variant는 서로 다른 형태의 데이터를 가질 수 있다. 근거: Defining an Enummatch는 가능한 경우를 빠짐없이 처리하도록 요구하고,if let은 특정 패턴 하나를 간단히 다룰 때 쓰는 축약형이다. 근거: match, Concise Control Flow with if lettrait는 여러 타입이 공유하는 동작 계약을 정의하는 수단이다. 근거: Traits: Defining Shared Behavior- 입문 실습 흐름은
cargo new프로젝트 기준으로 설명하는 편이 가장 재현하기 쉽다. 근거: Hello, Cargo!
직접 확인한 결과
1. 실습 프로젝트를 따로 만들어 예제를 교체하며 실행하는 흐름이 가장 단순했다
- 직접 확인한 결과: 아래처럼 새 Cargo 프로젝트를 만든 뒤
src/main.rs를 바꿔 가며cargo run을 반복하는 흐름이 가장 자연스러웠다.
cargo new rust-structs-enums-traits
cd rust-structs-enums-traits
code .
cargo run
2. struct와 impl은 데이터와 동작을 함께 읽게 해 줬다
- 직접 확인한 결과: 아래 예제로 struct 필드 묶기와 impl 메서드 호출을 한 번에 확인할 수 있었다.
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 20,
};
let rect2 = Rectangle {
width: 10,
height: 15,
};
println!("area = {}", rect1.area());
println!("can_hold = {}", rect1.can_hold(&rect2));
}
- 관찰된 결과:
area = 600
can_hold = true
3. enum, match, if let은 상태 표현과 분기 처리를 한 흐름으로 묶어 줬다
- 직접 확인한 결과: 아래 예제로 enum variant 분기와
if let의 축약된 패턴 처리를 함께 확인할 수 있었다.
enum Ticket {
Normal,
Vip(u32),
Staff(String),
}
fn main() {
let ticket = Ticket::Vip(3);
match ticket {
Ticket::Normal => println!("normal ticket"),
Ticket::Vip(level) => println!("vip level = {}", level),
Ticket::Staff(name) => println!("staff = {}", name),
}
let config_max = Some(5u8);
if let Some(max) = config_max {
println!("max = {}", max);
}
}
- 관찰된 결과:
vip level = 3
max = 5
4. trait를 포함한 종합 예제를 돌리면 네 개념의 역할 분담이 더 분명해졌다
- 직접 확인한 결과: 아래 예제처럼
struct,enum,match,trait를 한 파일에 모으면 “타입 설계”와 “공통 동작”의 연결이 잘 드러났다.
trait Summary {
fn summarize(&self) -> String;
}
#[derive(Clone, Copy)]
enum PostState {
Draft,
Published,
Archived,
}
struct Article {
title: String,
state: PostState,
}
impl Article {
fn new(title: &str, state: PostState) -> Self {
Self {
title: String::from(title),
state,
}
}
fn status_label(&self) -> &'static str {
match self.state {
PostState::Draft => "draft",
PostState::Published => "published",
PostState::Archived => "archived",
}
}
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{} [{}]", self.title, self.status_label())
}
}
fn main() {
let post = Article::new("Rust Structs and Traits", PostState::Published);
println!("summary = {}", post.summarize());
}
- 관찰된 결과:
summary = Rust Structs and Traits [published]
- 직접 확인한 결과: 일부 variant를 실제로 생성하지 않으면
dead_code계열 경고가 함께 출력됐다. 이는 예제 축약 때문에 생긴 경고이며, 개념 설명 자체가 틀렸다는 뜻은 아니었다.
해석 / 의견
- 해석:
struct,enum,match,trait는 각각 따로 외울 문법이라기보다 “데이터 모델링과 동작 설계”라는 하나의 문제를 나눠 맡는 도구에 가깝다. - 의견: 초급자 기준으로는
trait를 바로 추상화 문법으로만 보지 말고, “서로 다른 타입에 같은 인터페이스를 부여하는 약속”으로 이해하는 편이 부담이 적다. - 의견:
if let은match를 대체하는 문법이 아니라, 경우 하나만 빠르게 확인할 때 읽기 좋은 축약형으로 배우는 편이 실용적이다.
한계와 예외
- 이 글은 단일 파일 기준의 입문 예제만 다룬다. trait object, generic trait bound 심화, derive macro 활용, pattern guard는 범위 밖이다.
- 예제에 따라 unused variant 같은 경고가 보일 수 있으며, 경고 문구는 Rust 버전에 따라 조금 달라질 수 있다.
Option<T>와Result<T, E>는 enum의 대표 사례이지만, 이 글에서는 개념 연결만 짧게 언급하고 별도 심화는 하지 않는다.- macOS, Linux, WSL 환경 차이는 다루지 않았다.
댓글남기기