複数の実装ブロック
CoddyのRustジャーニー「Object Oriented Programming」セクションの一部 — レッスン 5/61。
Rustでは、構造体のメソッドを複数のimplブロックに分割することができます。すべてを1つのブロックにまとめることも可能ですが、分割することでコードの整理がしやすくなり、可読性が向上します。
以下は、その機能が2つの実装ブロックに分割された構造体です:
struct BankAccount {
balance: f64,
}
// コンストラクタと基本的なクエリ
impl BankAccount {
fn new(initial: f64) -> BankAccount {
BankAccount { balance: initial }
}
fn balance(&self) -> f64 {
self.balance
}
}
// トランザクションメソッド
impl BankAccount {
fn deposit(&mut self, amount: f64) {
self.balance += amount;
}
fn withdraw(&mut self, amount: f64) {
self.balance -= amount;
}
}
両方のブロックはシームレスに連携します。Rustはそれらを1つのものであるかのように扱います。どちらのブロックで定義されているかに関わらず、任意のメソッドを呼び出すことができます:
fn main() {
let mut account = BankAccount::new(100.0);
account.deposit(50.0);
println!("{}", account.balance()); // 150
}
このパターンは、構造体が大きくなるにつれて特に便利になります。コンストラクタをまとめたり、読み取り専用メソッドをミュータブル(変更可能)なメソッドから分離したり、機能ごとに整理したりすることができます。1つの構造体が持てるimplブロックの数に制限はありません。
チャレンジ
簡単2つのフィールド、name (String) と score (u32) を持つ Player 構造体を作成してください。
構造体のメソッドを 2つの別々の実装ブロック (implementation blocks) に分けて整理してください:
1つ目の impl ブロック — コンストラクタとクエリ:
new—name: Stringを受け取り、scoreを0に設定したPlayerを返す関連関数get_score—&selfを受け取り、現在のスコアをu32として返すメソッド
2つ目の impl ブロック — スコアの変更:
add_points—&mut selfとpoints: u32を受け取り、スコアにポイントを加算するメソッドreset_score—&mut selfを受け取り、スコアを0にリセットするメソッド
3つの入力を受け取ります:
- 1行目:プレイヤーの名前 (String)
- 2行目:第1ラウンドで加算するポイント (u32)
- 3行目:第2ラウンドで加算するポイント (u32)
Player::new を使用してプレイヤーを作成し、第1ラウンドのポイントを加算してスコアを表示します。その後、スコアをリセットし、第2ラウンドのポイントを加算して最終的なスコアを表示してください。
期待される出力形式:
{score_after_first_round}
{score_after_reset_and_second_round}チートシート
Rustでは、整理しやすくするために、構造体のメソッドを複数の impl ブロックに分割して記述することができます。
struct BankAccount {
balance: f64,
}
// First impl block
impl BankAccount {
fn new(initial: f64) -> BankAccount {
BankAccount { balance: initial }
}
fn balance(&self) -> f64 {
self.balance
}
}
// Second impl block
impl BankAccount {
fn deposit(&mut self, amount: f64) {
self.balance += amount;
}
fn withdraw(&mut self, amount: f64) {
self.balance -= amount;
}
}
異なる impl ブロックにあるすべてのメソッドは、シームレスに連携して動作します。
let mut account = BankAccount::new(100.0);
account.deposit(50.0);
println!("{}", account.balance()); // 150
1つの構造体が持てる impl ブロックの数に制限はありません。このパターンは、コンストラクタと状態を変更するメソッドを分けるなど、関連する機能をグループ化するのに役立ちます。
自分で試してみよう
use std::io;
// TODO: name (String) と score (u32) フィールドを持つ Player 構造体を定義してください
// TODO: 最初の impl ブロック - コンストラクタとクエリメソッド (new, get_score)
// TODO: 2番目の impl ブロック - スコア修正メソッド (add_points, reset_score)
fn main() {
let mut input = String::new();
// プレイヤー名を読み込む
io::stdin().read_line(&mut input).expect("Failed to read line");
let name = input.trim().to_string();
// 第1ラウンドのポイントを読み込む
input.clear();
io::stdin().read_line(&mut input).expect("Failed to read line");
let first_round: u32 = input.trim().parse().expect("Invalid number");
// 第2ラウンドのポイントを読み込む
input.clear();
io::stdin().read_line(&mut input).expect("Failed to read line");
let second_round: u32 = input.trim().parse().expect("Invalid number");
// TODO: Player::new を使用してプレイヤーを作成してください
// TODO: 第1ラウンドのポイントを加算し、スコアを表示してください
// TODO: スコアをリセットしてください
// TODO: 第2ラウンドのポイントを加算し、最終スコアを表示してください
}このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。