拡張トレイトを定義すべきでしょうか?
どのような場面で、フリー関数よりも拡張トレイトを優先すべきでしょうか?
// 著作権 2025 Google LLC // SPDX-License-Identifier: Apache-2.0 pub trait StrExt { fn is_palindrome(&self) -> bool; } impl StrExt for &str { fn is_palindrome(&self) -> bool { self.chars().eq(self.chars().rev()) } } // 比較 fn is_palindrome(s: &str) -> bool { s.chars().eq(s.chars().rev()) }
拡張トレイトの主な利点は、見つけやすさです。
-
見つけやすさ: 拡張メソッドは、フリー関数よりも見つけやすく なります。言語サーバー(例:
rust-analyzer)は、外部型のインスタンスの後に.を入力すると、それらを候補として提示します。 -
メソッドチェーン: 拡張トレイトの大きな使い勝手の向上として、メソッド チェーンがあります。これは
Iteratorトレイトの基盤であり、data.iter().filter(...).map(...)のような流れるような呼び出しを可能にします。 これをフリー関数で実現しようとすると、はるかに煩雑になります (map(filter(iter(data), ...), ...))。 -
ジェネリクスと
dyn: トレイトは、ジェネリクスのトレイト境界として 使ったり、dyn Traitの一部として使ったりできますが、フリー関数は必ずしも ジェネリックな文脈で使えるとは限りません。 -
API の一体性: 拡張トレイトは、まとまりのある API を作るのに役立ちます。 外部型に対して複数の関連関数(例:
is_palindrome,word_count,to_kebab_case)がある場合、それらを 1 つのStrExtトレイトに まとめる方が、ユーザーがインポートする複数のフリー関数を用意するよりも すっきりしていることがよくあります。 -
トレードオフ: こうした利点がある一方で、単純な関数が 1 つだけの場合には、 専用の拡張トレイトはやりすぎかもしれません。どちらのアプローチでも追加の インポートが必要であり、見慣れたメソッド構文という利点が、完全なトレイト定義の ための定型コードに見合わないこともあります。