🦀
타입 시스템
타입 시스템 · 선수: 소유권 1~5강
11. 구조체 (named·tuple·unit 구조체)
구조체(struct) 는 여러 값을 의미 있는 이름으로 묶는 가장 기본적인 사용자 정의 타입입니다. Rust 는 세 가지 형태를 제공합니다 — 필드에 이름이 있는 named struct, 위치만 있는 tuple struct, 데이터가 없는 unit struct. 메서드는 impl 블록에서 따로 정의합니다.
Ruststructimpl메서드타입 시스템
소요 시간
⏱ 약 1.5시간
난이도
📊 중급
선수 조건
🎯 소유권 1~5강
결과물
구조체(struct) 는 여러 값을 의미 있는 이름으로 묶는 가장 기본적인 사용자 정의 타입입니다. Rust 는 세 가지 형태를 제공합니다 — 필드에 이름이 있는 named struct, 위치만 있는 tuple struct, 데이터가 없는 unit struct. 메서드는 impl 블록에서 따로 정의합니다.
이 강의에서 배우는 것
- 1named / tuple / unit struct 를 각각 정의한다
- 2impl 블록으로 메서드와 연관 함수를 작성한다
- 3struct update 문법(`..base`) 으로 필드를 일부만 바꾼 새 인스턴스를 만든다
- 4`#[derive(Debug)]` 로 디버그 출력을 가능하게 한다
- 5&self / &mut self / self 메서드 시그니처를 구분한다
소개
튜플 `(f64, f64)` 로 점을 표현할 수도 있지만, `.0` `.1` 로 접근하면 의도가 불분명합니다. struct 는 같은 데이터에 **의미 있는 이름** 을 붙여 코드의 자기 설명력을 끌어올립니다.
핵심 개념
1) 세 가지 형태
rust
// named — 필드명 있음
struct Point { x: f64, y: f64 }
// tuple struct — 필드명 없이 타입만
struct Color(u8, u8, u8);
// unit struct — 데이터 없음, 마커 용도
struct Marker;2) impl — 메서드와 연관 함수
- **메서드** — 첫 인자가 `self`, `&self`, `&mut self`. 인스턴스가 호출함
- **연관 함수(associated function)** — self 가 없는 함수. `Type::func()` 으로 호출, 생성자 역할
3) self 의 세 가지 형태
| 수신자 | 의미 | 용도 |
|---|---|---|
| &self | 불변 참조 | 읽기 메서드 |
| &mut self | 가변 참조 | 쓰기 메서드 |
| self | 소유권 가져감 | 변환·소비 메서드 |
4) struct update 문법
`..base` 로 기존 인스턴스의 필드를 일부만 바꾼 새 인스턴스를 만들 수 있습니다.
핵심 예제
rust
#[derive(Debug, Clone)]
struct Point { x: f64, y: f64 }
impl Point {
// 연관 함수 — 생성자
fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
// 메서드 — &self 읽기
fn distance_from_origin(&self) -> f64 {
(self.x * self.x + self.y * self.y).sqrt()
}
// 메서드 — &mut self 쓰기
fn translate(&mut self, dx: f64, dy: f64) {
self.x += dx;
self.y += dy;
}
}
fn main() {
let mut p = Point::new(3.0, 4.0);
println!("{}", p.distance_from_origin()); // 5.0
p.translate(1.0, 1.0);
println!("{:?}", p); // Point { x: 4.0, y: 5.0 }
}tuple struct — 색상 같은 단순 wrapper:
rust
struct Color(u8, u8, u8);
fn main() {
let red = Color(255, 0, 0);
println!("R={}", red.0);
}struct update 문법:
rust
#[derive(Debug)]
struct User { name: String, email: String, active: bool }
fn main() {
let u1 = User { name: "홍길동".into(), email: "a@b.c".into(), active: true };
let u2 = User { email: "new@b.c".into(), ..u1 }; // name, active 는 u1 에서 복사/이동
println!("{:?}", u2);
}자주 하는 실수
Q. println!("{}", p) 가 안 돼요
A. 기본 struct 는 Display 가 구현돼 있지 않습니다. `#[derive(Debug)]` 후 `{:?}` 또는 `{:#?}` 로 출력하거나, `impl fmt::Display` 를 직접 작성.
Q. &self 와 self 중 뭘 쓰나요?
A. **읽기는 &self, 쓰기는 &mut self, 인스턴스를 변환·소비(예: into_xxx) 하는 메서드는 self**. 이 세 가지가 명확히 구분된다는 점이 Rust 디자인의 정수.
Q. tuple struct 와 단순 tuple 의 차이가 뭐죠?
A. tuple struct 는 **이름이 있는 새 타입** 이라 컴파일러가 구분합니다. `Meters(f64)` 와 `Feet(f64)` 가 서로 호환 불가 → 타입 오용 컴파일 시 차단.
정리
- named / tuple / unit 세 형태
- impl 블록에 메서드와 연관 함수
- self 의 세 형태 — &self / &mut self / self
- `..base` 로 일부만 바꾼 새 인스턴스 생성
과제
- Rectangle 구조체에 area / perimeter 메서드를 작성
- User 구조체에 update_email(&mut self, new: String) 메서드를 작성하고 호출
- 단위를 헷갈리지 않게 tuple struct Meters(f64) / Feet(f64) 를 만들고 변환 함수 작성