RustのTraitとかの話
底辺プログラマです(´・ω・`)
たまに趣味でプログラムしてます。
そして最近Rust始めました。
Rust難しいですよね。ライフタイムやらなにやらで、関数から&strで返そうとして発狂したり、Structの中にStructなメンバを作ったらサイズがわからねーから無理やで、みたいな事言われたり。
そんな中で、ふと困ったのがJavaとかででインターフェース(Interface)があって、複数実装してふるまい(動作)を分けるとかあるじゃないですか。
あれ、どうしたらいいんだ。ああそうだTraitだ。
と思ったのですが実現まで四苦八苦したのでメモ書きとして残します。
ゴール
こんな感じにして実行すると
fn main() { let mut ppp = Hoge::new(|| Fuga::new()); ppp.display(); ppp.display(); ppp.display(); ppp.display(); ppp.display(); ppp.display(); let mut ppp = Hoge::new(|| Huga {}); ppp.display(); }
こんな感じに出力される
Fuga One 1 Fuga One 2 Fuga One 3 Fuga One 4 Fuga One 5 Fuga One 6 Huga One
みたいな。
この↑のケースだとFugaとHugaが同じトレイトを実装しているイメージです。
Fugaでは内部にカウント持ち、カウントアップして出力、Hugaは単純に同じものを出力するという実装のつもりです。
Fuga&Huga
こちらはトレイトHageを持つようにします。
trait Hage { fn one(&mut self); }
そして実装は最小限で
struct Fuga { cnt: i32, } impl Fuga { fn new() -> Self { Self { cnt: 0 } } } impl Hage for Fuga { fn one(&mut self) { self.cnt += 1; println!("Fuga One {}", self.cnt); } } struct Huga ; impl Hage for Huga { fn one(&mut self) { println!("Huga One"); } }
としました。あとはHogeの実装です。実をいうと一番困ったのはこちらでした。
Hoge
こちらは色々とコンパイルエラーが出たりして、初心者にはとてもつらかったです(ノД`)・゜・。
最終的にBoxを使うことで解決しました。
struct Hoge<'a> { hage_t: Box, } impl<'a> Hoge<'a> { fn new<T, Z>(xxx: T) -> Self where Z: Hage + 'a, T: FnOnce() -> Z, { Self { hage_t: Box::new(xxx()), } } fn display(&mut self) { self.hage_t.one(); } }
んでもって
正直初心者でよくわからないまま作成しているのでもっとよい実装があるかもしれません。
なぜこういうものを作ろうかと思ったかというと、プログラムを部分的にMockに置き換えテストしたりしたいと思ったためです。
私自身、もう一度同じのを一から作れる気がしないのでこのブログでメモ書きとして残しました。そのため命名がひどいのは許してください_| ̄|○