見出し画像

Rust ジェネリック型

ジェネリック型について


型安全なのはいいけど・・・

Rustは型安全な言語ですが、同じような構造体を何度も定義するのは面倒です。
例えば、整数型の構造体と文字列型の構造体を定義すると、同じようなコードが繰り返されます。

struct IntStruct {
    value: i32,
}

struct StringStruct {
    value: String,
}

型の数だけ、同じような構造体を定義するのはめんどくさい・・・


ジェネリック型を使う

ジェネリック型を使うと、型に依存しない構造体を定義できます。これにより、同じコードを再利用でき、冗長性が減ります。

struct GenericStruct<T> {
    value: T,
}

`<T>`はジェネリック型パラメータで、任意の型を受け取ることができます。任意の型?どいうこと?と思うかもしれませんが、実際に使うときに型を指定します。


ジェネリック型の使用例

ジェネリック型を使って、整数型と文字列型の構造体を定義してみましょう。

fn main() {
    let int_struct = GenericStruct { value: 42 };
    let string_struct = GenericStruct { value: String::from("Hello") };

    println!("整数の値: {}", int_struct.value);
    println!("文字列の値: {}", string_struct.value);
}

1個の構造体で整数や文字列を扱えるので、コードがすっきりします。
これでちょっと型が違うだけで同じような構造体を何度も定義する必要がなくなります。


トレイトで区別する!

ジェネリック型を使うといっても、特定の機能を持つ型に制限したい場合があります。
例えばCloneを実装している型だけを受け入れたい場合、トレイト境界=>〇〇を実装しているもののみ!っていう制約をつける!を使います。

fn print_value<T: Clone>(value: T) {
    let cloned_value = value.clone();
    println!("値: {:?}", cloned_value);
}

この例では、`T: Clone`と書くことで、`T`が`Clone`トレイトを実装している型であることを指定しています。これにより、`print_value`関数は`Clone`トレイトを持つ型のみを受け入れます。


複数もOKです!

トレイト境界を使って、複数のトレイトを同時に指定することもできます。

fn print_value<T: Clone + std::fmt::Debug>(value: T) {
    let cloned_value = value.clone();
    println!("値: {:?}", cloned_value);
}

この例では、`T`が`Clone`トレイトと`Debug`トレイトの両方を実装している型であることを指定しています。これにより、`print_value`関数は`Clone`と`Debug`の両方の機能を持つ型のみを受け入れます。


Uも使える

ジェネリック型は複数の型パラメータを持つこともできます。
例えば、`T`と`U`という2つの型パラメータを持つ関数を定義することができます。

fn print_values<T: Clone + std::fmt::Debug, U: Clone + std::fmt::Display>(value: T, value2: U) {
    let cloned_value = value.clone();
    println!("値: {:?}", cloned_value);
    println!("値2: {}", value2);
}

この例では、`T`が`Clone`と`Debug`トレイトを実装している型、`U`が`Clone`と`Display`トレイトを実装している型であることを指定しています。これにより、`print_values`関数は、2つの異なる型を受け入れることができます。
※別にT,Uじゃなくてもいいです。A,B,C,DとかでもOKです。
ただなんか慣習的な?よくチュートリアルではT,Uが使われる。
理由は・・・調べてください。


where句を使う

トレイトが3、4個と増えてくると、関数の定義が長くなってしまいます。
その場合は、`where`句を使うと見やすくなります。

fn print_value<T, U>(value: T, value2: U)
where
    T: Clone + std::fmt::Debug + std::fmt::Display,
    U: Clone + std::fmt::Debug + std::fmt::Display,
{
    let cloned_value = value.clone();
    println!("値: {:?}", cloned_value);
}

このように、`where`句を使うことで、トレイト境界を関数の定義から分離し、コードをより読みやすくすることができます。

いいなと思ったら応援しよう!