メソッドと関数の違い
Rustでは「メソッド」と「関数」は明確に区別されます。
メソッドは関数と似ており、fn
キーワードを使って宣言します。ただし、メソッドは必ず何かしらのオブジェクト(構造体や列挙型などを指します)に紐づけられます。
メソッドを実装するときはimpl
キーワードを使用します。例えば構造体にメソッドを実装したい場合は、impl 構造体名 { ... }
とします。
メソッドの引数
メソッドの第一引数は必ず実装されているインスタンス自身になります。その引数名はself
で型はSelf
と表現されます。このself
は、メソッドが基づく構造体のインスタンスを指します。例えばPerson
という構造体にメソッドを定義した場合、そのメソッドの第一引数のself
はPerson
型になります。インスタンスのメソッドを呼び出すと、そのインスタンスの情報を参照したり、更新したりできます。
また、fn メソッド名(self: Self)
と書く代わりにfn メソッド名(self)
というシンタックスシュガーも用意されています。
更新する際は、&mut self
と書く必要があります。
struct Person {
name: String,
age: u8,
}
impl Person {
fn greet(self) {
println!("Hello, my name is {}.", self.name);
}
fn birthday(&mut self) {
self.age += 1;
println!("Happy birthday! I'm now {} years old.", self.age);
}
}
self
だけでなく、第二引数以降で追加の引数を取るメソッドも定義できます。
impl Person {
fn update_name(&mut self, new_name: String) {
self.name = new_name;
println!("My name is now {}.", self.name);
}
}
メソッドの呼び出し
インスタンス名.メソッド名()
の形で呼び出します。第一引数のself
は自動的に渡されるので、呼び出し時に明示する必要がありません。すなわち、self
しか引数を取らない場合は、()
だけで大丈夫です。第二引数以降がある場合、第二引数からメソッドに渡します。
fn main() {
let mut person = Person {
name: String::from("Alice"),
age: 30,
};
person.greet(); // "Hello, my name is Alice."
person.birthday(); // "Happy birthday! I'm now 31 years old."
person.update_name(String::from("Bob")); // "My name is now Bob."
}
構造体にロジックを持たせるという点で、強力なオブジェクト指向を実現できそうな気がします。
関連関数
メソッドの定義時には「第一引数はself
である」と書きましたが、そうでない関数を書くことも可能です。すなわち、第一引数がself
でない関数を定義でき、それは(メソッドではなく)関連関数と呼ばれます。
関連関数はあくまで関数であり、インスタンスではなく構造体そのものに紐づきます。呼び出すときは型名::関連関数名
で呼び出します(String::newやVector::new
のようなものです)。
impl Person {
// 関連関数
fn new(name: String, age: u8) -> Self {
Self { name, age }
}
}
主なユースケースですが、構造体のコンストラクタに利用することが考えられます。構造体は構造体名 { ... }
で宣言できますが、フィールドを個別に指定させるのが手間になります。生成時に指定したいフィールドとそうでないフィールド(自動計算したい、任意の値を取れない)がある場合は、そのロジックをコンストラクタとして定義しておき、そのコンストラクタを通してインスタンスを生成させるパターンが有効です。
fn main() {
let person = Person::new(String::from("Charlie"), 25);
person.greet(); // "Hello, my name is Charlie."
}
関連関数はJavaでいうstaticメソッドのようなものでしょうか。上手に使い分けたいですね。