あれやこれやと

自転車、プログラミング、登山など思いついたままに書いていこうと思っています

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に置き換えテストしたりしたいと思ったためです。

私自身、もう一度同じのを一から作れる気がしないのでこのブログでメモ書きとして残しました。そのため命名がひどいのは許してください_| ̄|○