問題の歴史
Rustはオブジェクト指向言語からメソッドの概念を借用していますが、それを異なる方法で実装しています。従来のthisやselfではなく、メソッドは明示的にselfパラメータを受け取ります。関連関数は、他のプログラミング言語における静的メソッドの代わりに登場しました。これらは型に関連していますが、特定の値には関連していません。
問題
メソッド、関連関数、および自由関数を混同することがよくあります。selfを持つメソッドとselfを持たない関連関数をいつ使用するかが明確でありません。可視性、オートデレファレンス、所有権の移動に関する問題もあります。
解決策
Rustのメソッドは、implブロック内で最初のパラメータとしてself、&self、&mut selfを持つように宣言されます(通常はstructやenumに対して)。それらはインスタンスで呼び出されます:object.method()。関連関数(例えばnewやfrom)は、同様にimpl内で最初のパラメータselfを持たずに宣言され、二重コロンを介して呼び出されます:Type::function()。
コード例:
struct Point { x: f64, y: f64, } impl Point { // 関連関数(コンストラクタ) fn new(x: f64, y: f64) -> Self { Self { x, y } } // メソッド:selfを要求 fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } let p = Point::new(3.0, 4.0); printf!("{}", p.distance_from_origin()); // 5.0
主な特徴:
::を介してのみ呼び出されます関連関数をインスタンスから呼び出すことはできますか(ドット記法で)?
可能ですが(例えばp.new(1.0, 2.0))、非常に推奨されません。なぜなら、関連関数は現在のオブジェクトにアクセスできず、インスタンスが無視されて渡されるため、混乱を招くからです。Type::func()シンタックスを使用することをお勧めします。
コード例:
let p = Point::new(1.0, 2.0); let q = p.new(0.0, 0.0); // 動作するがベストプラクティスではない!
メソッドは非同期にすることができますか?
はい。メソッドは自由関数と同様に、asyncキーワードを使って宣言できます:
impl Foo { async fn do_async(&self) { // ... } }
同じimplブロック内でメソッドと関連関数の両方を宣言できますか?
はい—任意の組み合わせが許可されています。また、1つの型に対して複数のimplブロックを宣言することも可能です。
初心者がnew関数をimplの外で宣言し、それをコンストラクタとして使用しようとして、インスタンス経由で誤って呼び出しました:p.new(1.0, 2.0)。
利点:
すぐに動作する(コンパイラが許可する)。
欠点:
コードが読みにくく、メンテナンスが困難で、正しいself所有権を持つメソッドを使用することが難しい。
すべてのメソッドと関連関数は厳密にimpl内で宣言され、呼び出しの正しいシンタックス(コンストラクタ用にType::new()、アクション用にobj.method())が使用されます。
利点:
高い可読性、ベストプラクティスに従っている。
欠点:
Rustのイディオムを知っている必要があり、シンタックスに注意が必要です。