Rustの「ジェネリックなデータ型」を再履修（ブログ邦訳）

# 概要
以下のdev.to記事の日本語訳です。
https://dev.to/talzvon/rust-generic-types-in-method-definitions-4iah
内容自体はRust公式チュートリアル10.1章に包含されますが、入門者が読み過ごしやすいポイントをより丁寧に説明しています。訳者による大幅な抜粋・追記・変更あり。

# 対象
- [Rust公式チュートリアル](https://doc.rust-jp.rs/book-ja/ch10-01-syntax.html) 10章でつまずいた人
- Rustで初めてジェネリクスの概念に触れた人

# 導入

もしあなたがRustの初心者で、Rustの公式チュートリアルを読んでいるのなら（もちろん読むべきです）、私のように[10.1章: ジェネリックなデータ型](https://doc.rust-jp.rs/book-ja/ch10-01-syntax.html)の章で混乱した経験があるかもしれません *（例: `impl<T> Struct<T> {}`ってなんで`<T>`を2回書くんだっけ？）*。その混乱を解消するために、この記事を書きました。すでに公式チュートリアルをその部分まで読んで、ジェネリックなデータ型についておぼろげには理解をしていることが前提になります。この記事では型定義とメソッド定義におけるジェネリック型の使い方について、初心者が犯しがちな間違いに注意しながら、順番にみていくことにしましょう。

# 最もシンプルな例

まずは一番簡単な例から。チュートリアルで紹介されているとおり、`struct`キーワードを使って、ユーザーが自前の型（Type）を定義することができます。それにメソッドを追加したい場合、次のようにしますよね。

```rust
// 型を宣言する
struct Container {
    field: i32,
}

// 型にメソッドを実装する
impl Container {
    fn foo(&self) {
        // 何らかの操作
    }
}
```
`impl`キーワードを使って、型にメソッドを追加しています。


:::note info
Rustにおいて、`struct`キーワードを使って定義される「構造体（struct）」は「型（type）」の1種です（「構造体であれば型である」は正しく、「型であれば構造体である」は誤り）。たとえばスカラー型（`i32`, `bool`, etc...）や複合型（タプル・配列）・列挙型などは構造体ではない型の代表例です。すべての型について、`impl`を用いたメソッドの実装が可能です。よって**本稿では構造体を例示しつつ、「型」という表現を主に使って説明を進めます**。
:::

# シンプルなジェネリック型の使用例

一方、構造体にジェネリックな型が含まれている場合、`impl`を行う際にも型を指定しなければなりません。下の例で、`[TYPE]`とは、`impl`ブロックによってメソッドを実装される（具体的な）型を指しています。

```rust
struct Container<T> {
    field: T,
}

impl Container<[TYPE]> {

}
```
例えば、次のような場合ですね。

```rust
struct Container<T> {
    field: T,
}

impl Container<i32> {
    fn foo(&self) {
        println!("これはContainer::<i32>::foo()です!");
    }
}
```
これの意味するところは、「`T`がどんな型であったとしても`Container`のインスタンスを作ることができるが、`T`として`i32`型を使ったインスタンスだけが`foo`メソッドを持つことができる」ということになります。実際にインスタンスを作成してみると、このことが確認できます。
```rust
let inst1 = Container { field: 'c' };
let inst2 = Container { field: 10 };
let inst3 = Container { field: 12.2 };

inst1.foo(); // コンパイルエラー:  - method doesn't exist
inst2.foo(); // 動作OK
inst3.foo(); // コンパイルエラー:  - method doesn't exist
```

これら3つのインスタンスはすべて`Container<T>`型ですが、インスタンス生成時にジェネリック型として`i32`を渡した`inst2`のみが`foo`メソッドを持ちます。

これはある種のパターンマッチと言っても良いでしょう。インスタンス生成時に渡される具体的な型（**matcher**）と、`impl`内で定義されたメソッドの型情報（**pattern**）とを照らし合わせて、matcherとpatternが適合した時のみインスタンスにメソッドが付与される訳です。


# 独自型を使ったシンプルな例

先ほどはプリミティブ型である`i32`を使いましたが、`struct`や`enum`等によって自分で定義した型を使っても同じことです。以下のコードは`i32`を独自型`Color`に変更した以外は、先ほどのコードと全く同じものです。

```rust
struct Color {
    red: u8,
    green: u8,
    blue: u8,
}

struct Container<T> {
    filed: T,
}

impl Container<Color> {
    fn foo(&self) {
        println!("これはContainer::<Color>::foo()です!");
    }
}

fn main() {
    let inst1 = Container {
        filed: Color { red: 10, green: 20, blue: 30 },
    };
    let inst2 = Container { field: 10 };
    let inst3 = Container { field: 12.2 };

    inst1.foo(); // 動作OK
    inst2.foo(); // コンパイルエラー:  - method doesn't exist
    inst3.foo(); // コンパイルエラー:  - method doesn't exist
}
```

**「インスタンス生成時にジェネリック型として`Color`を渡したインスタンスのみが`foo`メソッドを持つ」**。さっきと一緒ですね。


# よくある入門者のミス

さて、ここまで特定の型にマッチした時のみメソッドを実装したい場合を見てきました。では、**インスタンス生成時に`T`としてどのような型を与えた場合でもメソッドを付与させたい時**はどうすればよいのでしょうか。最初に思いつくのは、次のようなコードかもしれません:

```rust
struct Container<T> {
    field: T,
}

impl Container<T> {
    fn foo(&self) {
        println!("これはContainer::<ANYTYPE>::foo()です??");
    }
}
```

**残念ながら、これは期待どおりには動きません**。何がおかしいのでしょうか？ `Color`型を使った例と比較して、コンパイラがどう解釈するのかを忠実に再現してみてください: **「インスタンス生成時にジェネリック型として`T`を渡したインスタンスのみが`foo`メソッドを持つ」**。

つまり、このコードは **「Tという名前の（具体的な）型」** に対して`foo`メソッドを付与しようとするのです。そんな名前の型はこのコードのどこにも定義されていないので、「Tなんて名前の型は見当たらないよ」というメッセージとともにコンパイルエラーという事になります。「私は`Container<T>`の部分で`T`をジェネリック型引数として定義したよ！」と言うかもしれませんが、その定義のスコープは`struct`ブロックの中だけです。`impl`ブロックからすれば知る由もありません。

要はここまでの情報では片手落ちだったのです。まだ型定義の際（`struct`ブロック）のジェネリクスの使い方を学んだだけでした。我々は新たにメソッド定義の際（`impl`ブロック）のジェネリクスの使い方を学ぶ必要があります。

# 全ての型にマッチするimplブロック

じゃあどうすればあなたは`impl`ブロックがすべての型に対してマッチすることをコンパイラに伝えられるでしょうか？ 
答え: `impl`ブロックの中でもジェネリック型を定義してやれば良いのです。

```rust
struct Container<T> {
    field: T,
}

impl<T> Container<T> {
    fn foo(&self) {
        println!("これはContainer::<ANYTYPE>::foo()です!");
    }
}
```

ここでは、`impl<T>`でまずジェネリック型`T`を定義して、それから`Container<T>`とすることでインスタンスがどんな型を`T`として持った場合でもマッチするように指定しました。トレイト境界（[公式本10.2章](https://doc.rust-jp.rs/book-ja/ch10-02-traits.html)で学びます）による制約なしで、あらゆる型です。結果として、インスタンス生成時に`T`としてどのような型を与えた場合でもメソッド`foo`が付与されることになります。

これで、なぜ`impl`行の中で`<T>`を2回書く必要があるのか、お分かり頂けたかと思います。**1回はジェネリック型引数を定義するため**、**もう1回はそれを使ってpatternを構築するため**だったという訳です。

:::note info
結局、何が混乱の元だったのでしょうか？それはおそらく、`impl`では**ジェネリック型引数の宣言**（`impl<T>`）とジェネリック型を用いた**patternの構築**（`Cotainer<T>`）が分離していたのに対し、`struct`では**型引数宣言**と**pattern構築**を同時に行っている（`struct Container<T>`）という違いからでしょう。

ではなぜ`struct<T> Container<T>`のような書き方はしないのか？答えは「意味がないから」になるでしょう。`struct Container<T>`の`<T>`の部分に`i32`のような具象型が来る可能性はありません。よって、この部分はその型で使用するジェネリック型引数を、使用する数だけ並べる以外にやりようがなく、すなわちそれはジェネリック型引数を宣言することと同義だからです。ですから、`struct Container<T>`と書かれた時点で`<T>`がジェネリック型であることは**自明**であり、わざわざ`struct<T> Container<T>`のように別途`T`がジェネリック型引数であることを宣言する必要は無い、冗長である、であれば省略してしまえ、という訳です。

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160540/258b0b24-77e5-a1ef-59f6-31ff9e358c51.png)
:::



# （おまけ）ジェネリクスを書く時のルール

`impl`の振る舞いに慣れるために、ジェネリック型を扱う時に覚えておくて良いいくつかのルールを紹介しましょう（訳注: 正確に言えばルールではなく、ルールから導かれるいくつかの重要な特徴）。

## ルール1: 
- **`impl`定義内のmatcherの数は、型定義内のジェネリック型の引数の数と一致しなければならない**

例えば下の例はもちろん上手く行きます。
```rust
struct Container<T, V> { }
impl Container<i32, i32> { } 
```
`struct`の定義には2つのジェネリック型引数`T`, `V`があるので、implブロックのmatcherの数は絶対に「2」でなければなりません。つまり下の例はダメです。
```rust
struct Container<T, V> { }
impl Container<i32> { }
```
次の例もダメです。
```rust
struct Container<T, V> { }
impl Container { }
```

## ルール2
- **`impl`定義内のジェネリック型引数の数は、型定義内のジェネリック型の引数の数と一致しなくても良い**

これはうまくいきます（型定義では2つのジェネリック型引数、impl定義では0）。
```rust
struct Container<T, V> { }
impl Container<i32, i32> { }
```

下の例も同じくOKです（型定義では2つのジェネリック型引数、impl定義では1）。
```rust
struct Container<T, V> { } 
impl<T> Container<T, i32> { }
```

下の例も当然OKです（型定義では2つのジェネリック型引数、impl定義では2）。
```rust
struct Container<T, V> { }
impl<T, V> Container<T, V> { } 
```

## ルール3

- **`impl`定義内のジェネリック型引数の名前は、型定義内のジェネリック型の名前と一致しなくても良い**

下の例は問題なく動作し、これまでの例と全く同じ振る舞いをします。
```rust
struct Container<T, V> { }
impl<A, B> Container<A, B> { }
```

## 型同士の関係性に対するマッチング

型定義内に複数のジェネリック型がある場合、それらに対して「具体的な型」をマッチングさせるだけでなく、それらジェネリック型の「関係性」に対してマッチングを行うことも可能です。
```rust
struct Container<T, V> { }
impl<X> Container<X, X> { }
```
上の例では、`X`という一つのジェネリック型を`impl<X>`で定義し、そして`Container<X, X>`とすることによって、ジェネリック型が両方とも同じ型であることを要求しています。**どんな型でも良いから、とにかく`T`と`V`が同じ型である場合のみ、この`impl`ブロックが有効化される**、ということです。くどいようですが、これは`T`と`V`が異なる型である場合にインスタンスを「作れない」という意味ではありません。インスタンスは作れます。ただ、そのインスタンスに対してこの`impl`ブロックは有効ではない、というだけです。



# 参考サイト
- https://dev.to/talzvon/rust-generic-types-in-method-definitions-4iah
- https://users.rust-lang.org/t/difference-between-type-and-struct/29733/4
- https://doc.rust-lang.org/reference/types.html