複数の項目をひとまとめにして、そのまとまりを1つの単位として扱えると便利なことがよくあります。Rustでは、そのようなまとまりを構造体と呼び、まとまりに含まれる各項目を構造体のフィールドと呼びます。構造体は利用できるフィールドの一般的な集合を定義しますが、構造体の具体的な実例はインスタンスと呼ばれます。
さらに、構造体にはメソッドを定義でき、そのメソッドはフィールドにアクセスできます。その場合、構造体自身はselfと呼ばれます。メソッドが&mut selfを使うときは、フィールドを変更できます。つまり可変にできます。メソッドが&selfを使うときは、フィールドを変更できません。不変です。可変性を制御することで、Rustでは並行性バグのいくつもの種類がそもそも起こらないと、借用チェッカーが保証しやすくなります。
この演習では、構造体に2種類のメソッドを実装します。1つ目は一般にゲッターと呼ばれ、構造体のフィールドを外部に公開しますが、ほかの誰かがその値を変更することはできません。Rustでは、これらのメソッドは慣例的に、公開するフィールドと同じ名前を付けます。つまり、nameという構造体のフィールドを取得するゲッターなら、メソッド名は単にname()になります。
また、別の種類のメソッドとして、一般にセッターと呼ばれるものも実装します。これはフィールドの値を変更します。セッターはRustではあまり一般的ではありません。フィールドを自由に変更できるなら、単に公開するほうが一般的です。ただし、フィールドの更新に副作用がある場合や、アクセス制御のために役立ちます。たとえば、pub(crate)と付けたセッターを使うと、同じクレート内のほかのモジュールがプライベートなフィールドを更新できます。このフィールドは外部から影響を受けることはありません。
構造体には3つの種類があります。名前付きフィールドを持つ構造体、タプル構造体、ユニット構造体です。このコンセプト演習では、最初の種類である名前付きフィールドを持つ構造体を扱います。
構造体は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!
楽しんで取り組みましょう!