セッター
CoddyのRustジャーニー「Object Oriented Programming」セクションの一部 — レッスン 12/61。
ゲッターを使うと、外部のコードがプライベートなデータを読み取ることができます。しかし、そのデータを変更させる必要がある場合はどうすればよいでしょうか?そこで登場するのがセッターメソッドです。これは、プライベートなフィールドを制御された方法で変更するパブリックメソッドです。
基本的なセッターは、&mut self と新しい値を受け取ります:
// wallet.rs
pub struct Wallet {
balance: f64,
}
impl Wallet {
pub fn new(initial: f64) -> Wallet {
Self { balance: initial }
}
pub fn balance(&self) -> f64 {
self.balance
}
pub fn set_balance(&mut self, amount: f64) {
self.balance = amount;
}
}
ゲッターとは異なり、Rustのセッターは通常、データを変更することを明確に示すために set_ プレフィックスを使用します。&mut self パラメータは不可欠です。これにより、メソッドに構造体のフィールドを変更する権限が与えられます。
セッターの真の力はバリデーション(検証)にあります。単にどんな値でも受け入れるのではなく、ルールを強制することができます:
pub fn set_balance(&mut self, amount: f64) {
if amount >= 0.0 {
self.balance = amount;
}
// 負の値は暗黙的に無視されます
}
これでウォレットは自らを保護するようになりました。外部コードがどのような値を設定しようとしても、balance(残高)が負になることはありません。これがカプセル化の真髄です。struct(構造体)が自身の不変条件(invariants)を制御するため、外部コードがそれを無効な状態にすることは決してできません。
チャレンジ
簡単データの妥当性を維持するために、セッターがどのようにルールを強制できるかを示すサーモスタットシステムを構築しましょう。このサーモスタットには、安全な範囲内でのみ調整可能な温度設定機能を持たせます。
コードを整理するために、2つのファイルを作成します:
thermostat.rs: プライベートなtemperatureフィールド (i32) を持つThermostat構造体を定義します。構造体はパブリックにする必要がありますが、無効な値から保護するためにフィールドはプライベートのままにします。以下を実装してください:- 初期温度を受け取る
newコンストラクタ - 現在の温度を返す
temperatureゲッター - 10から30(両端を含む)の間の値のみを受け入れる
set_temperatureセッター。値がこの範囲外の場合、温度は変更されないようにします。
- 初期温度を受け取る
main.rs: サーモスタットモジュールを取り込み、サーモスタットを作成します。様々な温度の設定を試み、各試行後の結果をプリントすることで、セッターのバリデーションを実証してください。
セッターは無効な値を黙って無視する必要があります。エラーメッセージは表示せず、有効な範囲外の値を設定しようとした場合は、現在の温度をそのまま維持してください。
出力は以下の形式に従う必要があります:
Initial: {temperature}
After setting to {attempted_value}: {temperature}
After setting to {attempted_value}: {temperature}例えば、温度 20 でサーモスタットを作成し、次に 25 (有効) に設定しようとし、その後に 50 (無効) に設定しようとした場合、出力は以下のようになります:
Initial: 20
After setting to 25: 25
After setting to 50: 25入力として、初期温度、1回目の試行温度、2回目の試行温度の3つを受け取ります。
チートシート
セッターメソッドを使用すると、外部コードが制御された方法でプライベートフィールドを変更できるようになります。これらは、構造体のデータを変更する権限を得るために &mut self を受け取ります。
基本的なセッターの構文:
pub fn set_balance(&mut self, amount: f64) {
self.balance = amount;
}Rustのセッターは通常、データを変更することを明確に示すために set_ プレフィックスを使用します。
セッターは、ルールを強制しデータの整合性を維持するためのバリデーション(検証)を可能にします:
pub fn set_balance(&mut self, amount: f64) {
if amount >= 0.0 {
self.balance = amount;
}
// 無効な値は暗黙的に無視されます
}これはカプセル化の実践です。構造体が自身の不変条件を制御し、外部コードが構造体を無効な状態にすることを防ぎます。
自分で試してみよう
mod thermostat;
use thermostat::Thermostat;
fn main() {
// 入力を読み込む
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let initial_temp: i32 = input1.trim().parse().expect("Invalid number");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let first_attempt: i32 = input2.trim().parse().expect("Invalid number");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let second_attempt: i32 = input3.trim().parse().expect("Invalid number");
// TODO: 初期温度でサーモスタットを作成する
// TODO: 初期温度を表示する
// 形式: "Initial: {temperature}"
// TODO: 1つ目の温度の設定を試行し、結果を表示する
// 形式: "After setting to {attempted_value}: {temperature}"
// TODO: 2つ目の温度の設定を試行し、結果を表示する
// 形式: "After setting to {attempted_value}: {temperature}"
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。