여러 항목을 하나로 묶어서 그 묶음을 하나의 단위로 다루면 유용할 때가 많아요. Rust에서는 이런 묶음을 구조체라고 부르고, 묶음 안의 각 항목을 구조체의 필드라고 불러요. 구조체는 사용할 수 있는 필드의 일반적인 집합을 정의하고, 구조체의 특정한 한 예는 인스턴스라고 불러요.
게다가 구조체에는 필드에 접근할 수 있는 메서드를 정의할 수 있어요. 이때 구조체 자신을 self라고 불러요. 메서드가 &mut self를 사용하면 필드를 바꿀 수 있어요. 즉 가변(mutate)이 가능해요. 메서드가 &self를 사용하면 필드를 바꿀 수 없어요. 불변이에요. 이렇게 가변성을 제어하는 덕분에 빌림 검사기는 온갖 종류의 동시성 버그가 Rust에서는 아예 발생하지 않도록 보장할 수 있어요.
이 연습 문제에서는 구조체에 두 종류의 메서드를 구현해 볼 거예요. 첫 번째는 흔히 게터라고 하는데, 구조체의 필드를 바깥에 드러내되 다른 누구도 그 값을 바꾸지 못하게 해요. Rust에서는 이런 메서드가 드러내는 필드와 이름을 똑같이 맞추는 게 관례예요. 예를 들어 name이라는 구조체 필드를 가져오는 게터 메서드가 있다면, 그 메서드는 그냥 name()이라고 부르면 돼요.
또 다른 종류의 메서드도 구현해 볼 텐데, 흔히 세터라고 불러요. 세터는 필드의 값을 바꿔요. 세터는 Rust에서 그렇게 흔하지는 않아요. 필드를 마음대로 수정할 수 있다면 그냥 공개로 두는 편이 더 흔하거든요. 그래도 필드를 갱신할 때 부수 효과가 따라야 하거나 접근을 제어해야 할 때는 유용해요. pub(crate)로 표시한 세터는 같은 크레이트 안의 다른 모듈이 비공개 필드를 갱신할 수 있게 해 줘요. 이런 필드는 바깥세상에서는 건드릴 수 없어요.
구조체에는 세 가지 종류가 있어요. 이름 있는 필드를 가진 구조체, 튜플 구조체, 유닛 구조체예요. 이 개념 연습 문제에서는 첫 번째, 즉 이름 있는 필드를 가진 구조체를 살펴볼 거예요.
구조체는 struct 키워드 뒤에 그 구조체가 설명하는 타입의 이름을 대문자로 시작해 붙여서 정의해요:
struct Item {}
그다음 구조체 본문 안에 필드 로 여러 타입을 가져오는데, 각 필드는 저마다 타입을 가져요:
struct Item {
name: String,
weight: f32,
worth: u32,
}
마지막으로 구조체에는 impl 블록 안에서 메서드를 정의할 수 있어요:
impl Item {
// initializes and returns a new instance of our Item struct
fn new() -> Self {
todo!()
}
}
구조체 문법에 대한 짧은 소개는 여기까지 하고, 이제 이 연습 문제의 안내를 한번 살펴봐요!
건강 모니터링 시스템을 구현하는 작업을 하고 있어요. 그 일환으로 사용자의 건강 통계를 계속 추적해야 해요.
impl 블록 안에는 아직 내용이 채워지지 않은 스텁 함수들이 있고, 다음과 같은 구조체 정의도 있어요:
pub struct User {
name: String,
age: u32,
weight: f32,
}
목표는 impl 블록에 정의된 User struct의 스텁 메서드들을 구현하는 거예요.
예를 들어 new 메서드는 지정한 이름, 나이, 몸무게 값을 가진 User 구조체의 인스턴스를 반환해야 해요.
let mut bob = User::new(String::from("Bob"), 32, 155.2);
// Returns: a User with name "Bob", age 32, and weight 155.2
weight 메서드는 User의 몸무게를 반환해야 해요.
bob.weight();
// Returns: 155.2
set_age 메서드는 User의 나이를 설정해야 해요.
bob.set_age(33);
// Updates Bob's age to 33; happy birthday Bob!
재미있게 해봐요!