Go言語の関数で型パラメータ(ジェネリクス)を使う基本
生徒
「Go言語で同じような関数を複数の型で使い回す方法ってありますか?」
先生
「はい、Go1.18以降では『ジェネリクス』という仕組みを使って、関数に型パラメータを渡すことができるようになりました。」
生徒
「型パラメータってなんですか?」
先生
「型パラメータとは、関数や構造体に『型』を後から指定できるしくみのことです。同じ処理を整数や文字列など、さまざまな型で使いたいときにとても便利です。」
生徒
「なるほど!具体的な使い方を教えてください!」
先生
「では、Go言語の型パラメータの基本を見ていきましょう。」
1. ジェネリクスとは何か?
ジェネリクス(generics)とは、1つの関数や構造体を、違う型でもそのまま使い回せるようにするしくみです。たとえば、整数を表示する関数と、文字列を表示する関数を別々に作ると、同じようなコードが増えてしまいます。そこで、ジェネリクスを使うと、型だけを後から差し替えられるため、同じ処理を共通化できてスッキリ書けます。Go1.18以降で利用できる新しい機能なので、Go言語でも現代的なプログラミングができるようになりました。
「型パラメータ」という箱を用意しておき、関数を使うときに「この型で使いたい」と指定するイメージです。型が違っても同じロジックを再利用できるので、同じような関数を何度も作る必要がなくなります。
生徒
「ジェネリクスって、具体的にどんな場面で使うと便利なんですか?」
先生
「たとえば『値を表示するだけの関数』を考えてみましょう。同じ表示処理でも、整数も文字列も浮動小数点数もありますよね?ジェネリクスを使えば、型ごとに関数を分けなくても、1つの関数で全部に対応できます。」
生徒
「なるほど!型が違っても同じ処理なら、ジェネリクスにまとめられるということですね!」
先生
「その通りです。コードが少なくなるだけでなく、後から型を増やしても関数を書き直さなくていいというメリットがあります。」
2. 型パラメータを使った基本的な関数の書き方
次のように、関数名の後に[T any]のように書いて型パラメータを指定します。Tは任意の名前で、anyはすべての型を許容する制約です。
package main
import "fmt"
func PrintValue[T any](value T) {
fmt.Println(value)
}
func main() {
PrintValue[int](100)
PrintValue[string]("こんにちは")
}
この例では、PrintValue関数が整数でも文字列でも使えるようになっています。
3. 型推論を活用しよう
Go言語は型パラメータを自動で推測できるため、呼び出し時に型を明示しなくても動作します。
func main() {
PrintValue(123) // intと推論
PrintValue("Go言語") // stringと推論
}
4. 制約(constraint)を使って特定の型だけ許可する
型パラメータにconstraint(制約)を指定すると、特定の型に限定できます。たとえば数値型だけ許可したい場合は、constraintsパッケージを使います。
import (
"fmt"
"golang.org/x/exp/constraints"
)
func Double[T constraints.Integer](x T) T {
return x * 2
}
このようにすると、整数型のみが許可され、文字列などは使えなくなります。
5. 複数の型パラメータを使う例
ジェネリクスでは、複数の型パラメータも扱えます。以下の例では、2つの値を受け取ってペアにします。
type Pair[A, B any] struct {
First A
Second B
}
func main() {
p := Pair[int, string]{First: 1, Second: "Apple"}
fmt.Println(p)
}
構造体にも型パラメータを使えることがわかります。
6. 型パラメータを使うときの注意点
- 型パラメータの記述には角カッコ
[]を使います。 - Go1.17以前ではジェネリクスは使えません。必ずGo1.18以降で試してください。
- コードが複雑になりすぎないように、シンプルな関数から始めましょう。