こんにちは。KOUKIです。
とあるWeb系企業で、Go言語エンジニアをしています。
先日、Go1.18がリリースされてGenericsが使えるようになりました。
今回は、勉強を兼ねて、Genericsを実装してみたいと思います。
<目次>
Update
最初にGo言語のUpdateを行います。
1 2 |
$ go version go version go1.17 darwin/amd64 |
私の環境では、HomeBrewでGo言語をインストールしているので、brewコマンドでUpdateをしたいと思います。
1 2 3 4 |
brew upgrade go $ go version go version go1.18 darwin/amd64 |
OKですね。
事前準備
下記のファイルに、コードを実装していこうと思います。
1 |
touch main.go |
Genericsとはなんなのか
そもそもGenericsとはなんぞ?と思うかもしれません。
その疑問をコードを書きながらお答えします。
Sum関数を実装する
例えば、下記のようなコードがあるとします。
1 2 3 4 5 6 7 8 9 10 11 12 |
package main import "fmt" func main() { fmt.Println(sum(1, 2)) } func sum(v1 int, v2 int) int { return v1 + v2 } |
sum関数はint型の引数を二つ取り、合計した値を呼び出し元に返します。
そのため、このプログラムを実行すると以下の結果が得られます。
1 2 |
$ go run main.go 3 |
単純明快ですね^^
別の型を渡したい時は?
ここで一つ疑問が残ります。
「もしint型以外の型を渡したい場合はどうするのか?」です。
例えば、float64の型の場合は?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package main import "fmt" func main() { fmt.Println(fmt.Sprintf("sum = %d", sum(1, 2))) fmt.Println(fmt.Sprintf("sumFloat64 = %g", sumFloat64(1.3, 2.4))) } func sum(v1 int, v2 int) int { return v1 + v2 } func sumFloat64(v1 float64, v2 float64) float64 { return v1 + v2 } |
当然上記のように、新しい関数を定義しますよね。
1 2 3 |
$ go run main.go sum = 3 sumFloat64 = 3.7 |
なんだか冗長な気がしません?
ここでGenericsの出番!
この問題を解決するために、Genericsを実装します。
1 2 3 4 |
// Add Generics func sumGenerics[T any](v1, v2 T) T { return v1 + v2 } |
こちら新しい書式なので、少し戸惑うかもしれません。
Tには、型を指定します。anyは新しいGo言語の型です。
これで問題ないように見えますが、以下のエラーが出ます。
1 2 |
var v1 T invalid operation: operator + not defined on v1 (variable of type T constrained by any)compilerUndefinedOp |
anyだと型を判断できないので、+オペレーターを使った加算処理ができないようです。
対処方法は、こうします。
1 2 3 |
type sumTypes interface { float64 | int } |
こんな宣言も可能になったのですね。このInterfaceをsumGenericsに指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
func main() { fmt.Println(fmt.Sprintf("sum = %d", sum(1, 2))) fmt.Println(fmt.Sprintf("sumFloat64 = %g", sumFloat64(1.3, 2.4))) fmt.Println("-----------------------------------") fmt.Println(fmt.Sprintf("sumGenerics = %d", sumGenerics(1, 2))) fmt.Println(fmt.Sprintf("sumFloat64Generics = %g", sumGenerics(1.3, 2.4))) } type sumTypes interface { float64 | int } func sumGenerics[T sumTypes](v1, v2 T) T { return v1 + v2 } |
これでエラーが消えたので、プログラムを実行します。
1 2 3 4 5 6 |
$ go run main.go sum = 3 sumFloat64 = 3.7 ----------------------------------- sumGenerics = 3 sumFloat64Generics = 3.7 |
OKですね。
このように、IntやFloatなどの型に代わりにTを宣言し、呼び出しもとで柔軟に型を選択できるようにしたものが、Genericsのようですね。
まとめ
Genericsを使うと、型違いの関数を一つにまとめることが可能です。
これは、Genericsの使用方法の一つであり、全てではないと思っています。
Go言語以外での型付け言語の経験があまりないので、有効な使い方が思い浮かばないのですが、わざわざ実装されたということはたくさんの人が要望した機能なのでしょう。
Go言語はシンプルに実装できることが特徴な言語であるので、この機能の追加により、プログラムが複雑化しないか心配ではありますね^^;
それでは、また!
Go言語まとめ

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package main import ( "fmt" ) func main() { fmt.Println(fmt.Sprintf("sum = %d", sum(1, 2))) fmt.Println(fmt.Sprintf("sumFloat64 = %g", sumFloat64(1.3, 2.4))) fmt.Println("-----------------------------------") fmt.Println(fmt.Sprintf("sumGenerics = %d", sumGenerics(1, 2))) fmt.Println(fmt.Sprintf("sumFloat64Generics = %g", sumGenerics(1.3, 2.4))) } type sumTypes interface { float64 | int } // Add Generics func sumGenerics[T sumTypes](v1, v2 T) T { return v1 + v2 } func sum(v1 int, v2 int) int { return v1 + v2 } func sumFloat64(v1 float64, v2 float64) float64 { return v1 + v2 } |
コメントを残す
コメントを投稿するにはログインしてください。