문자열과 슬라이스로 데이터를 안전하게 빌릴 수 있게 되었으니, 이제 여러 값을 하나의 타입으로 묶을 차례입니다. Rust의 struct는 연관된 데이터를 의미 있게 이름 붙여 묶는 도구이고, [[impl|impl 블록]]은 그 데이터에 대한 행동을 정의합니다. 이번 글에서는 학습 진행도를 추적하는 작은 구조체를 만들고, 불변·가변 참조 규칙이 메서드에도 똑같이 적용된다는 것을 확인합니다.
구조체 정의하기
struct StudyLog {
topic: String,
completed: bool,
notes: Vec<String>,
}
fn main() {
let mut log = StudyLog {
topic: String::from("borrowing"),
completed: false,
notes: vec![String::from("불변 참조 복습")],
};
println!("{} 진행 중", log.topic);
log.notes.push(String::from("가변 참조 예제 작성"));
println!("노트 개수: {}", log.notes.len());
}
구조체 필드는 기본적으로 공개되지 않습니다. 이 예제는 같은 모듈 안에서만 사용하므로 그대로 접근할 수 있지만, 보통은 메서드를 통해 값을 읽고 수정하도록 설계합니다.
impl 블록과 메서드
메서드를 정의할 때는 첫 번째 인자로 self, &self, &mut self 중 하나를 선택합니다. 각 선택은 ownership과 borrowing 규칙을 그대로 반영합니다.
&self: 빌려서 읽기만 함&mut self: 빌려서 수정함- self: 값을 완전히 가져감
impl StudyLog {
fn new(topic: &str) -> Self {
Self {
topic: topic.to_string(),
completed: false,
notes: Vec::new(),
}
}
fn add_note(&mut self, memo: &str) {
self.notes.push(memo.to_string());
}
fn summary(&self) -> String {
format!("{} / 완료 여부: {} / 메모 {}건",
self.topic,
self.completed,
self.notes.len(),
)
}
fn finish(self) -> StudyLog {
StudyLog {
completed: true,
..self
}
}
}
fn main() {
let mut log = StudyLog::new("strings");
log.add_note("String vs &str 구분");
log.add_note("슬라이스 안전 범위");
println!("{}", log.summary());
let finished = log.finish();
// println!("{}", log.summary()); // 컴파일 오류: log는 finish로 이동됨
println!("완료 상태: {}", finished.summary());
}
new는 소유권을 가진StudyLog를 반환합니다.add_note는 가변 참조로 호출되므로, 동시에 다른 곳에서 불변 참조를 사용할 수 없습니다.summary는 불변 참조만 필요하므로&self를 사용합니다.finish는self를 값으로 받기 때문에 ownership을 가져오고, 메서드가 끝나면 기존 인스턴스를 더 이상 쓸 수 없습니다.
메서드 안에서 슬라이스와 참조 재사용하기
문자열과 슬라이스 개념을 구조체 메서드 안으로 옮겨오면 다음과 같이 동작합니다.
impl StudyLog {
fn latest_note(&self) -> Option<&str> {
self.notes.last().map(|note| note.as_str())
}
}
fn main() {
let mut log = StudyLog::new("structs");
log.add_note("필드 초기화 연습");
log.add_note("impl 블록 만들기");
if let Some(text) = log.latest_note() {
println!("최근 메모: {}", text);
}
}
Option<&str>는 구조체가 문자열을 소유한 채, 외부에는 슬라이스 참조만 빌려줍니다. 즉, 메모를 복사해서 새 문자열을 만드는 대신 "있으면 빌려주고, 없으면 None"으로 표현하는 방식입니다. 이 패턴은 소유권과 참조 규칙을 동시에 만족시켜, 불필요한 복사를 줄입니다.
CodeSandbox로 이어서 실습하기
아래 샌드박스는 CodeSandbox의 Rust starter입니다. 이번 글의 핵심 코드를 src/main.rs에 옮기고, cargo check와 cargo run 결과를 나란히 보면서 컴파일 메시지와 실행 출력을 비교해 보세요.
💬 댓글
이 글에 대한 의견을 남겨주세요