Open8

Goのジェネリックとは何ぞや

MarnyさんMarnyさん

Generics とは

Generics:「相称性」
データの方に束縛されず、型そのものをパラメータ化して扱うことができる。
型を抽象化してコードの再利用性を向上させつつ、静的型付け言語の型安全性を維持できる

参考:https://zenn.dev/nobishii/articles/type_param_intro

要するに

同じ処理だけど、引数の型が違うよっていう処理を一つの関数にまとめちゃうよっていう便利君。

MarnyさんMarnyさん

Goにジェネリックがあれば…みたいに言われてたらしく、それで実装したっぽい

MarnyさんMarnyさん

Goのジェネリクスの基本原則

  • 「関数」と「型」は「型パラメータ」を持つことができる
  • 「型パラメータ」の満たす性質は「インターフェース型」を「型制約」として使うことで表す
MarnyさんMarnyさん
func main() {
	fmt.Println(f([]MyInt{1, 2, 3, 4}))
    // Output:
    // [1 2 3 4]
}

// fは型パラメータを持つ関数
// Tは型パラメータ
// インタフェースStringerは、Tに対する型制約として使われている
func f[T Stringer](xs []T) []string {
	var result []string
	for _, x := range xs {
        // xは型制約StringerによりString()メソッドが使える
		result = append(result, x.String())
	}
	return result
}

type Stringer interface {
	String() string
}

type MyInt int

// MyIntはStringerを実装する
func (i MyInt) String() string {
	return strconv.Itoa(int(i))
}
MarnyさんMarnyさん

つまり、何ができるようになったのか

type Stringer interface {
    String() string
}

func f(xs []Stringer) []string {
    // いい感じの処理
}

type MyInt int

// MyIntはStringerを実装する
func(i MyInt) String() string {
    return strconv.Itoa(int(i))
}

# こいつを実行してみると、できない。
xs := []MyInt{0,1,2}
f(xs) // fは[]Stringerを受け付ける

こんな感じでMyIntをStringerとして実装(文字列で返却される形)
関数fはstringを引数としているが、MyIntはintなのでエラー

このとき、関数fを以下にする

func f(xs interface{})  {
	fmt.Println(xs)
}

そしたら動く。でも、interfaceを引数にしているので、型が間違っていても関数が走ってしまう。

MarnyさんMarnyさん

つまりこうゆうことや

package main

import (
	"fmt"
	"strconv"
)

func main() {
	fmt.Println(f([]MyInt{1, 2, 3, 4}))
	fmt.Println(f([]MyString{"a","b"}))
    // Output:
    // [1 2 3 4]
}

// fは型パラメータを持つ関数
// Tは型パラメータ
// インタフェースStringerは、Tに対する型制約として使われている
func f[T Stringer](xs []T) []string {
	var result []string
	for _, x := range xs {
        // xは型制約StringerによりString()メソッドが使える
		result = append(result, x.String())
	}
	return result
}

type Stringer interface {
	String() string
}

type MyInt int

type MyString string

func (s MyString) String() string{
	return string(s)
}

// MyIntはStringerを実装する
func (i MyInt) String() string {
	return strconv.Itoa(int(i))
}
MarnyさんMarnyさん

で、どんな時に使うんやろうな

  • interface{}の代わりに使われるだろうな
  • ymlとかのファイル生成に便利そう
    • 型の指定がないから