day 37 @rust trait②

・traitを関数の引数にする

fn func(tr: &impl MyTrait) {
    // do something.
}

・上記の方法は糖衣構文で本来の書き方はこう

fn func<T: MyTrait>(tr: &T) {
    // do something
}

・もしTだけでなくU,Rと複数あった場合見にくいので後ろで宣言する方法がある。それはwhereを使う方法である。

fn func<T, U>(tr_1: &T, tr_2: &U)
    Where T: MyTraitA,
                U: MyTraitB {
    // do something
}

・引数が複数要素の型をもつ場合は+演算子で表現可能

fn func<T: MyTrait + MyStruct>(tr: T) { }
fn func(tr: &(impl MyTrait + MyStruct)) { }

・条件付きの実装。
上記の糖衣構文、そうでない宣言方法、where、+は普通のジェネリクス型にも適用できる。
そして適用した場合特定の型にだけ有効な実装ができる。

fn main() {
    let ms = MyStruct {
        foo: 12,
        bar: 34,
        baz: 35,
    };
    ms.func(); // -> 81
    let ms = MyStruct {
        foo: String::from("foo"),
        baz: String::from("baz"),
        bar: String::from("bar"),
    };
    ms.func(); // -> 9
}

struct MyStruct<T> {
    foo: T,
    baz: T,
    bar: T,
}

impl MyStruct<i32> {
    fn func(&self) {
        println!("{}",&self.foo + &self.baz + &self.bar);
    }
}

impl MyStruct<String> {
    fn func(&self) {
        println!("{}", &self.foo.len() + &self.bar.len() + &self.baz.len());
    }
}

・ブラケット実装
任意のトレイトを実装したすべての構造体もしくはトレイトに対してトレイトを実装する。

impl<T: TraitA> TraitB for T { }

この場合TraitAを実装したすべてのデータ型に対してTraitBを追加で実装するという意味になる。

・戻り値にTraitを使う
fn func() -> impl MyTrait {}
ただしこのとき戻り値を返すステートメントが複数ある際には、それらはすべて同一データ型である必要がある。