LoginSignup
46

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-03-18

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

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

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

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

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

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
}

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

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

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

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

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46