0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rust】ジェネリクスの型パラメータってどこに書いたらいいの

Posted at

Rustを学び始めると、ジェネリクス(総称型)の概念とその記法に戸惑うことがあります。特に、ジェネリック型パラメータをどこに配置するべきか—implブロックの後ろに置くべきか、それとも個々のメソッド定義に置くべきか—という点は混乱しやすいです。

ジェネリクスの基本的な配置ルール

Rustでジェネリクスを使用する場合、その配置には下記のルールがあることを認識するとよいです:

  1. 構造体全体で使用するジェネリック型パラメータは、構造体名の後に配置します
  2. 実装全体(implブロック内の複数のメソッド)で共有されるジェネリック型パラメータは、implキーワードの後に配置します
  3. 特定のメソッドでのみ使用される型パラメータは、そのメソッド名の後に配置します

コード例

例1: 構造体とimplブロックのジェネリクス

2つの値を持つPoint構造体の例です:

struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn new(x: X1, y: Y1) -> Self {
        Point { x, y }
    }
    
    fn get_x(&self) -> &X1 {
        &self.x
    }
    
    fn get_y(&self) -> &Y1 {
        &self.y
    }
}

ここでは、X1Y1というジェネリック型パラメータを構造体定義で使用し、同じパラメータをimplブロックでも使用しています。これらのパラメータはimplブロック内のすべてのメソッドで共有され、メソッド間で一貫性のある型として扱われます。

例2: メソッド固有のジェネリクス

次に、特定のメソッドだけで使用するジェネリック型パラメータの例を見てみましょう:

struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

この例では、mixupメソッドが別のPointインスタンスを引数に取り、新しいPointを返します。ここで重要なのは:

  • X1Y1は構造体とimplブロック全体で共有される型パラメータ
  • X2Y2mixupメソッド固有の型パラメータで、このメソッドの中でのみ有効

このメソッドは2つの異なるPointインスタンスの値を組み合わせて、新しいPointインスタンスを作成します。

なぜimplブロックにすべての型パラメータを置けないのか?

どうせならimplブロックで全ての型パラメータを指定しておけばいいと思いませんか?しかし次のようなコードを書くとエラーに遭遇します:

// このコードはコンパイルエラーになります
struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1, X2, Y2> Point<X1, Y1> {
    fn mixup(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

このコードはコンパイルできません。エラーメッセージは以下のようになります:

error[E0207]: the type parameter `X2` is not constrained by the impl trait, self type, or predicates
 --> src/main.rs:6:12
  |
6 | impl<X1, Y1, X2, Y2> Point<X1, Y1> {
  |            ^^ unconstrained type parameter

なぜエラーになるのか?

このエラーが発生する理由は、Rustの型システムの設計に関係しています:

  1. implブロックで宣言されたジェネリックパラメータは、「制約される(constrained)」必要があります
  2. 「制約される」とは、そのパラメータが実装対象の型(この場合はPoint<X1, Y1>)、トレイト境界、またはwhere節のいずれかで使用されることを意味します
  3. X2Y2はこれらのいずれにも登場しないので「制約されていない型パラメータ(unconstrained type parameter)」としてエラーになっています

エラーメッセージが指摘している通り、X2Y2implブロックのどこにも「束縛」されていません。これらのパラメータは各メソッド呼び出しごとに異なる型を取る可能性があり、implブロックレベルで固定することができないと考えると、理解しやすいかもしれません。

まとめ

Rustにおけるジェネリクスの配置ルールをまとめると:

  1. 構造体全体で共有される型パラメータは構造体名の後に配置
  2. implキーワードの後に配置される型パラメータはimplブロック内のすべてのメソッドで共有される
  3. 特定のメソッドでのみ使用される型パラメータは、そのメソッド名の後に配置

この区別を理解することで、Rustのジェネリクスをより効果的に活用できるようになりますし、コードの読解力が向上すると思います!

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?