GolangでGenerics的なコンテナライブラリの制作で苦労をしたが勉強になった

ココ最近Goを勉強しはじめて
Go力（ごうりき） 低いですが　ちょっとずつ勉強していきます

#Genericsについて
GolangにはGenericsはありません
Genericsとは大雑把にいうと 色々な型での共通処理を1つのコードで行います

例えば、Listとかのコンテナライブラリが有名ですね
本来なら型ごとに

```cpp
IntList ilist;
ilist.insert(1);

StringList slist;
slist.insert("hoge");

DoubleList dlist;
dlist.insert(1.0);



class IntList{
private:
    int *begin;
    int *end;
public:
    void insert(int);
...
;

class StringList{
private:
    string *begin;
    string *end;
public:
    void insert(string);
...
;


class DoubleList{
private:
    double *begin;
    double *end;
public:
    void insert(double);
...
};
```

のように　型ごとに関数を作る必要があるのが


```cpp
List<int> ilist;
ilist.add(1);

List<string> slist;
slist.add("hoge");

List<double> dlist;
dlist.add(1.0);


template<class T>
class List<T>{
private:
    T *begin;
    T *end;
public:
    void insert(T);
...
};
```

と、複数型があっても　1つの関数書けばよくなり
開発工数もメンテナンス性もあがり とても便利

ところがGoには ジェネリクスがありません！！
Go2になればジェネリクスが導入されるという噂はありますが
現時点では存在しないので　なんとかしたいところです

そういうのは interfaceを使えば出来そうなので　勉強ついでに　作ってみようと思いました


#Contains
今回は Containsを作ろうと思います
与えられた配列内に特定の要素が存在すればTrue,しなければFalse を返します

##IntVal作戦
パッと思いついた作戦です。
Goにはclassはありませんが interfaceと structがあります
継承はないけど ダックタイピングができます
ということで 
ベースのInterfaceを作り、ダックタイピングしてみます

```golang:lib
type Traits interface {
	Val() Traits
}

type IntVal struct{val int}
func (u IntVal) Val() Traits {
	return (u)
}

type StringVal struct{val string}
func (u StringVal) Val() Traits {
	return (u);
}

func Contains(arr []Traits, val Traits) bool{
	for _, v := range arr{
		if v == val{
			return true
		}
	}
	return false
}

```


```golang:main
	va := []Traits  {IntVal{1},IntVal{2},IntVal{3},IntVal{4}}
	log.Printf( strconv.FormatBool( Contains( va, IntVal{1})) )
	log.Printf(strconv.FormatBool(Contains( va, IntVal{100})))

	vs := []Traits  {StringVal{"1"},StringVal{"2"},StringVal{"3"},StringVal{"4"}}
	log.Printf( strconv.FormatBool( Contains( vs, StringVal{"1"})) )


----
2018/12/02 03:10:15 true
2018/12/02 03:10:15 false
2018/12/02 03:10:15 true
```

IntVal、StringValという型？を作り interfaceで処理するという
最初に思いついたやりかたですが
不満ありますよね

Goには型変換コンストラクタがないので
 va := []Traits  {IntVal{1},IntVal{2},IntVal{3},IntVal{4}}
と　いちいち　明示的に型変換が必要で面倒

##interfaceで抽象化すればええやん？
更に抽象化をすすめる
実は　IntValとかは作らなくてよかった（メタプロの癖）

```golang:lib
func Contains(arr []interface{}, val interface{}) bool{
	for _, v := range arr{
		if v == val{
			return true
		}
	}
	return false
}
```

```golang:main
	va := []int  {1, 2, 3, 4}
	log.Printf( strconv.FormatBool( Contains( va, 1)) )
	log.Printf(strconv.FormatBool(Contains( va, 100)))
```

interface最高！ ジェネリクスなくてもなんとかなりそう

と思うだろ？
“cannot use va (type []int) as type []interface {} in argument to Contains”

ゔぁ〜〜〜！
![popute.jpg](https://qiita-image-store.s3.amazonaws.com/0/13916/727661e8-6757-5615-a966-0f1aecaf075e.jpeg)

任意の型から interface{}への変換はできるが
配列から []interface{}への変換は　ダメだそう

メモリマップの問題なのか　いちど []interface{}配列へコピーする必要がある

https://golang.org/doc/faq#convert_slice_of_interface

```golang:main
	va := []int  {1, 2, 3, 4}

	list := make([]interface{}, 0)
	for _, v := range va {
		list = append(list, v)
	}

	log.Printf( strconv.FormatBool( Contains( list, 1)) )
	log.Printf(strconv.FormatBool(Contains( list, 100)))

	vs := []string  {"1", "2", "3", "4"}
	list = make([]interface{}, 0)
	for _, v := range vs {
		list = append(list, v)
	}

	log.Printf( strconv.FormatBool( Contains( list, "1")) )


2018/12/02 04:16:57 true
2018/12/02 04:16:57 false
2018/12/02 04:16:57 true

```
いちどコピーしなおせば動くけどめんどくさい・・・

##配列コピーUtil

```golang:lib
func CopyInt(arr []int) []interface{} {
	list := make([]interface{}, 0)
	for _, v := range arr {
		list = append(list, v)
	}
	return list
}

func CopyString(arr []string) []interface{} {
	list := make([]interface{}, 0)
	for _, v := range arr {
		list = append(list, v)
	}
	return list
}

func Contains(arr []interface{}, val interface{}) bool{
	for _, v := range arr{
		if v == val{
			return true
		}
	}
	return false
}
```

```golang:main
	va := []int  {1, 2, 3, 4}
	log.Printf( strconv.FormatBool( Contains( CopyInt(va), 1)) )
	log.Printf(strconv.FormatBool(Contains( CopyInt(va), 100)))

	vs := []string  {"1", "2", "3", "4"}
	log.Printf( strconv.FormatBool( Contains( CopyString(vs), "1")) )

2018/12/02 04:16:57 true
2018/12/02 04:16:57 false
2018/12/02 04:16:57 true

```

Copyする関数をライブラリ化したよ。でも []interface{}が渡せないので型ごとにCopy関数作るよ

まー　かなりましになったけど、色々と惜しいよ
型が増えるごとにCopy関数作らないとだめだし
Copy発生して　効率わるい
このへんが　GenericsのないGoの限界なのか？？

##switchでCopyまとめる
配列自体を interface{}で受け取り それを型でSwitch Caseで処理分岐させる

```golang:lib

func Copy(arr interface{}) []interface{} {
	dest := make([]interface{}, 0)

	switch val := arr.(type) {
	case []interface{}:
	case []int:
		for _, v := range val {
			dest = append(dest, v)
		}
	case []string:
		for _, v := range val {
			dest = append(dest, v)
		}
	case int:
		dest = append(dest, val)
	case string:
		dest = append(dest, val)
	}

	return dest
}
```

```golang:main

	va := []int  {1, 2, 3, 4}
	log.Printf( strconv.FormatBool( Contains( Copy(va), 1)) )
	log.Printf(strconv.FormatBool(Contains( Copy(va), 100)))

	vs := []string  {"1", "2", "3", "4"}
	log.Printf( strconv.FormatBool( Contains( Copy(vs), "1")) )

2018/12/02 04:53:05 true
2018/12/02 04:53:05 false
2018/12/02 04:53:05 true

```

呼び出し側からは Copyメソッド1つで扱えるので便利だ
Switch CaseのdefaultでCopyしたいと思うが　どうやらGoでは defaultでは foreachさせてもらえないので
型ごとに書くしか無い
つまり　型が増えると　この関数を修正しなければならない

##ContainsにCopyをまとめる
曖昧になってあまり好きではないが Containsに渡すものすら interface{}にしてしまう

```golang:lib
func Copy(arr interface{}) []interface{} {
	dest := make([]interface{}, 0)

	switch val := arr.(type) {
	case []interface{}:
	case []int:
		for _, v := range val {
			dest = append(dest, v)
		}
	case []string:
		for _, v := range val {
			dest = append(dest, v)
		}
	case int:
		dest = append(dest, val)
	case string:
		dest = append(dest, val)
	}

	return dest
}

func Contains(i interface{}, val interface{}) bool{
	arr := Copy(i)

	for _, v := range arr{
		if v == val{
			return true
		}
	}
	return false
}
```

```golang:main
	va := []int  {1, 2, 3, 4}
	log.Printf( strconv.FormatBool( Contains( va, 1)) )
	log.Printf(strconv.FormatBool(Contains( va, 100)))

	vs := []string  {"1", "2", "3", "4"}
	log.Printf( strconv.FormatBool( Contains( vs, "1")) )

2018/12/02 05:01:30 true
2018/12/02 05:01:30 false
2018/12/02 05:01:30 true

```

エラー処理（アサーションしたり） などを考えなければ　かなりましになってきた
いまだにCopyにおいて 型が増えるとライブラリコード追加というダメなんだけど
これが ジェネリクスのないGolangの限界かな？
  

##型チェック
本来ならGenericsを使っていれば厳密な型チェックができるため
Contain（int［］， int） はOKであるが
Contain(int[],string)　はコンパイル時にエラーさせられる

```cpp
template<class T>
bool Contain(T[], T);
```

他の言語でも上記のように 2つの引数の型が厳密に定義できるが
Goでは 両方共interfaceでなんでも入ってしまうため

```golang
	va := []int  {1, 2, 3, 4}
	log.Printf( strconv.FormatBool( Contains( va, "1")) )
```

と型をミスしてしまうと、この場合 コンパイルエラーも実行時エラーもでず Falseになる
のでContainsにて型チェックを入れてみる
本来ならこういう場合には Assertを使い、デバッグビルド時には例外が発生するが本番コードには影響あたえないようにするが
GolangにはAssertがないため Panicで問答無用でおとす

このあたりGo開発者は
Assertが便利なのは認めるが　お前らは正しく使わないだろ？ エラー処理を正しく使わないだろ？
だからGoにはAssertは入れないぜ

とのことで、正しく使いたいのに困った。。

```golang:lib
func Contains(i interface{}, val interface{}) bool{
	arr := Copy(i)

	if(reflect.TypeOf(arr[0]) != reflect.TypeOf(val)){
		log.Printf("missmatch Type in Contains [%s] and [%s]",reflect.TypeOf(arr[0]) ,reflect.TypeOf(val) )
		panic(0)
	}

	for _, v := range arr{
		fmt.Println( reflect.TypeOf(arr), reflect.TypeOf(v), reflect.TypeOf(val))
		if v == val{
			return true
		}
	}
	return false
}

```

本来なら 引数の配列と 検索要素を比較して []intとint　であればOK
という形にしたいが　Goには配列の要素型を比較する方法がみつからず
また　[]interface{}　になっており比較ができなかったので　1つ目の要素と比較しているが
意図通り 型間違えると panicをおこす


#オチ
https://golang.org/src/container/list/list.go

container/list　使いましょう