동작하는 코드
사용자 정보를 저장한다고 생각해보세요. 이름, 나이, 이메일이 필요해요. 지금까지 배운 방법으로는 변수 세 개를 따로 만들어야 했어요. 구조체(struct) 를 쓰면 하나로 묶을 수 있어요!
#[derive(Debug)]
struct User {
name: String,
age: u32,
email: String,
}
fn main() {
let user = User {
name: String::from("민수"),
age: 25,
email: String::from("minsu@example.com"),
};
println!("{:?}", user);
println!("이름: {}", user.name);
}
#[derive(Debug)]를 붙이면 {:?}로 구조체 전체를 출력할 수 있어요. 아직 자세한 의미는 몰라도 돼요 — 지금은 "디버깅용 출력을 가능하게 해주는 마법"이라고 생각하세요.
직접 수정하기
User에is_active: bool필드를 추가하고, 값을true로 설정해보세요.user.name을 수정하려면 어떻게 해야 할까요?user.name = String::from("지수");를 시도해보세요. 어떤 에러가 나나요?
"왜?" — 구조체와 소유권
구조체는 소유권의 연장이에요. User가 String 필드를 가지면, 그 구조체가 문자열의 주인이에요. 구조체가 사라지면 필드의 값도 함께 정리돼요.
필드 수정하기
구조체의 값을 바꾸려면 변수 자체가 mut이어야 해요. Rust는 일부 필드만 가변으로 만드는 걸 허용하지 않아요 — 전체가 가변이거나 전체가 불변이에요.
#[derive(Debug)]
struct User {
name: String,
age: u32,
}
fn main() {
let mut user = User {
name: String::from("민수"),
age: 25,
};
user.age = 26; // mut이니까 수정 가능!
println!("{:?}", user);
}
메서드 추가하기 — impl 블록
구조체에 동작을 추가하고 싶으면 impl 블록을 사용해요.
#[derive(Debug)]
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
// 메서드: &self로 자기 자신을 빌림
fn area(&self) -> f64 {
self.width * self.height
}
fn is_square(&self) -> bool {
self.width == self.height
}
// 연관 함수: self가 없음 (생성자 패턴)
fn square(size: f64) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn main() {
let rect = Rectangle { width: 10.0, height: 5.0 };
println!("넓이: {}", rect.area());
println!("정사각형? {}", rect.is_square());
let sq = Rectangle::square(7.0);
println!("정사각형 넓이: {}", sq.area());
}
&self, &mut self, self — 빌림 규칙의 연장
메서드의 첫 번째 매개변수가 self의 형태를 결정해요. 이전 모듈에서 배운 빌림 규칙 그대로예요!
| 매개변수 | 의미 | 비유 |
| ----------- | --------------------- | ------------------ |
| &self | 읽기만 함 (불변 빌림) | 도서관에서 책 읽기 |
| &mut self | 수정도 함 (가변 빌림) | 편집 권한으로 수정 |
| self | 소유권을 가져감 | 가져가서 안 돌려줌 |
#[derive(Debug)]
struct Counter {
count: i32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
fn value(&self) -> i32 {
self.count // 읽기만
}
fn increment(&mut self) {
self.count += 1; // 수정
}
}
fn main() {
let mut c = Counter::new();
c.increment();
c.increment();
println!("카운트: {}", c.value());
}
increment는 값을 바꾸니까 &mut self, value는 읽기만 하니까 &self를 사용해요.
더 알아보기: 구조체에 &str을 넣으면?
구조체 필드에 &str을 넣으려고 하면 이런 에러가 나요.
struct User {
name: &str, // 에러!
}
error[E0106]: missing lifetime specifier
빌린 값(&str)을 구조체에 넣으려면 수명(lifetime) 을 지정해야 해요. "이 빌린 값이 얼마나 오래 유효한지" 컴파일러에게 알려줘야 하거든요.
수명은 모듈 15에서 배울 거예요. 지금은 구조체 필드에는 String을 쓰는 게 안전하다고 기억해두세요!
연습 1. Book 구조체를 만들고 summary 메서드를 추가해보세요.
// TODO: Book 구조체 정의 (title: String, author: String, pages: u32)
// TODO: #[derive(Debug)] 추가
// TODO: impl 블록에 summary 메서드 추가
// "《제목》 - 저자 (N쪽)" 형태의 String을 반환
fn main() {
let book = Book {
title: String::from("Rust 프로그래밍"),
author: String::from("Dale"),
pages: 300,
};
println!("{}", book.summary());
// 출력: 《Rust 프로그래밍》 - Dale (300쪽)
}
힌트: format! 매크로를 사용하면 편해요.
연습 2. Counter 구조체에 reset 메서드를 추가해보세요. count를 0으로 되돌려야 해요.
impl Counter {
// ... 기존 메서드 ...
fn reset(&mut self) {
// 여기를 완성하세요
}
}
힌트: 값을 수정하니까 어떤 종류의 self가 필요할까요?
Q1. 구조체의 필드를 수정하려면?
- A) 필드 이름 앞에
mut을 붙인다 - B) 변수를
let mut으로 선언한다 - C)
#[derive(Mutable)]을 추가한다 - D) 필드 타입 앞에
mut을 붙인다
Q2. impl 블록에서 &self의 의미는?
- A) 구조체의 소유권을 가져간다
- B) 구조체를 불변으로 빌린다 (읽기만 가능)
- C) 구조체를 가변으로 빌린다 (수정 가능)
- D) 새로운 구조체를 만든다
Q3. Rectangle::square(5.0)처럼 self 없이 호출하는 함수를 뭐라고 하나요?
- A) 메서드
- B) 연관 함수
- C) 클로저
- D) 매크로