interface{} な変数を型が決まっている関数の引数にする

  • 24
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

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

func v(x interface{}){
  fmt.Println(x)
}

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

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

func main(){
  ver 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式を使うことで、型を変換して関数に渡すことができます。

func main(){
  ver x interface{} = 1
  ver 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
}

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

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

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

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時に、エラーを出してほしいのですが、なかなかそうもいかない…

参考