go语言将interface变量转换为固定类型的参数

终有一天 我有属于我的天

Goのプログラミングでは、どんな型の引数でも受け取れる関数を作るのにinterface{}を使います。

1
2
3
4
5
6
func v(x interface{}){
fmt.Println(x)
}

// v(1) => 1
// v("abc") => abc

しかし、interface{}型な変数を、たとえばintを引数にとる関数に渡すと、エラーになります。

1
2
3
4
5
6
7
8
9
func main(){
var x interface{} = 1
fmt.Println( add2(x) )
//=> cannot use x (type interface {}) as type int in argument to add2: need type assertion
}

func add2(n int) int{
return n + 2
}

このようなことをしたい場合は、castするか、switch式を使うことで、型を変換して関数に渡すことができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main(){
var x interface{} = 1
var y interface{} = 5

// cast
if xi, ok := x.(int); ok{
fmt.Println( add2(xi) ) //=> 3
}

//switch
switch yi := y.(type){
case int:
// ここに入ってきた時は、yiの型はintとして扱われる
fmt.Println( add2(yi) ) //=> 5
case int64, int32, int8:
// ここに入ってきた時は、yiの型はint64, int32, int8のうち適切な型が選ばれている
}
}

func add2(n int) int{
return n + 2
}

パッケージ的なものを自作していると、

  • どんな型の変数も渡せる
  • 特定の型の関数を渡せる

ような関数が欲しくなることがあります。具体的には以下のように使える関数です

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func exec(
v interface{}, // value
f interface{}, // valueを引数に実行するfunc
) interface{} {
fv := reflect.ValueOf(f)
if fv.Kind() != reflect.Func{
panic("2'nd argument is not func.")
}
rv := reflect.ValueOf(v)
return fv.Call([]reflect.Value{rv})[0]
}

// 以下のように使える
exec(5, func(i int) int{ return i * ( i + 1) })
exec("Tom", func(s string) string{ return "hello! " + s })

// 第一引数と、第二引数の引数の型が違うと、buildはできるが実行時にエラーが起きる
exec("Tom", func(i int) int{ return i * ( i + 1) })

build時に、エラーを出してほしいのですが、なかなかそうもいかない…

https://qiita.com/umanoda/items/07887d33ef1155b26ed2