はじめに
こんにちは。細々とプログラミングをしているsotanengelです。
この記事は以下の記事の連載です。
他の連載記事 (詳細)
- Day 1:型システムを使ってデータ構造を再現しよう
- Day 2:型システムを用いて共通の挙動を表現しよう
- Day 3:OptionとResultに対してはmatchを用いずに変換しよう
- Day 4:標準のErrorを使おう
- Day 5:型変換を理解しよう
- Day 6:newtypeパターンを活用しよう
- Day 7:複雑な型にはビルダを使おう
- Day 8:明示的なループの代わりにイテレータ変換を使用することを検討しよう
- Day 9:標準トレイトに習熟しよう
- Day 10:RIIパターンにはDropトレイトを実装しよう
- Day 11:ジェネリクスとトレイトオブジェクトのトレードオフを理解しよう
- Day 12:デフォルト実装を用いて、実装しなければならないトレイトメソッドを最小限にしよう
また本記事はEffective Rust(David Drysdale (著), 中田 秀基 (翻訳))を参考に作成されております。とてもいい書籍ですので興味を持った方は、ぜひ読んでみてください!
今日の内容
概要
Rustでトレイトを実装する際には、必須メソッド
とデフォルト実装
を定義することができます。
幅広い人々に使ってもらえるようにトレイト設計時に考慮するべきことを学びましょう。
必須メソッドとデフォルト実装を自作のトレイトに実装しよう
問(リンク)
自作のMyTrait
トレイトにrequired_method
という必須メソッドを実装し、デフォルト実装のdefault_method
をオーバーライドしてください。
コード (詳細)
// MyTrait トレイトの定義
trait MyTrait {
// TODO: required_methodという必須メソッドを作成してください。
// 引数は&selfとしてください。
fn default_method(&self) {
println!("default_method: これはトレイトで定義されたデフォルト実装です。");
}
fn default_method_with_return(&self) -> i32 {
println!("default_method_with_return: これはトレイトで定義されたデフォルト実装です。");
42
}
}
// トレイトを実装する構造体
struct MyStruct {
pub name: String,
}
// MyStruct における MyTrait の実装
impl MyTrait for MyStruct {
// required_method はトレイトで必須とされているので、必ず定義が必要
fn required_method(&self) {
println!("required_method: Hello, my name is {}!", self.name);
}
// default_method はデフォルトメソッドだが、ここでは独自実装でオーバーライド
// TODO: default_methodをオーバーライドしてください。
// 「default_method (override): 構造体 MyStruct 独自の実装です。オーバーライドされました。」という出力をしてください。
fn default_method(&self) {}
}
fn main() {
let my_struct = MyStruct {
name: String::from("Alice"),
};
// 【必須メソッド】呼び出し
my_struct.required_method();
// 【デフォルトメソッド(オーバーライド済)】呼び出し
my_struct.default_method();
// 【デフォルトメソッド(戻り値あり)】デフォルトの実装を呼び出し
let result = my_struct.default_method_with_return();
println!("default_method_with_return の戻り値: {}", result);
}
解答(リンク)
コード参照。
このように「必要最小限の必須メソッド」と「多数のデフォルト実装」というトレイトを実装することで、
- トレイトの実装者:少ないコーディングでトレイトを適用できる
- トレイトの使用者:様々なメソッド(デフォルト実装)を利用できる
を両立することができます。
また、このような実装にすることでトレイトに機能を追加した場合でもメソッドを安全に追加することができるため、トレイトの後方互換性を保証しやすくなります。
コード (詳細)
// MyTrait トレイトの定義
trait MyTrait {
// 【必須メソッド】
// トレイトを実装する型は、このメソッドを必ず定義しなければなりません。
fn required_method(&self);
// 【デフォルトメソッド】
// 必須ではありません。必要に応じてオーバーライドできます。
fn default_method(&self) {
println!("default_method: これはトレイトで定義されたデフォルト実装です。");
}
// 【デフォルトメソッド(戻り値あり)】
// 必須ではありません。必要に応じてオーバーライドできます。
fn default_method_with_return(&self) -> i32 {
println!("default_method_with_return: これはトレイトで定義されたデフォルト実装です。");
42
}
}
// トレイトを実装する構造体
struct MyStruct {
pub name: String,
}
// MyStruct における MyTrait の実装
impl MyTrait for MyStruct {
// required_method はトレイトで必須とされているので、必ず定義が必要
fn required_method(&self) {
println!("required_method: Hello, my name is {}!", self.name);
}
// default_method はデフォルトメソッドだが、ここでは独自実装でオーバーライド
fn default_method(&self) {
println!(
"default_method (override): 構造体 {} 独自の実装です。オーバーライドされました。",
self.name
);
}
// default_method_with_return はデフォルト実装のまま使いたい場合は定義不要。
}
fn main() {
let my_struct = MyStruct {
name: String::from("Alice"),
};
// 【必須メソッド】呼び出し
my_struct.required_method();
// 【デフォルトメソッド(オーバーライド済)】呼び出し
my_struct.default_method();
// 【デフォルトメソッド(戻り値あり)】デフォルトの実装を呼び出し
let result = my_struct.default_method_with_return();
println!("default_method_with_return の戻り値: {}", result);
}
さいごに
もしも本リポジトリで不備などあれば、リポジトリのissueやPRなどでご指摘いただければと思います。