sgykfjsm.github.com

自分で定義したprimitiveな型を扱う際の注意点

Goではよく知られているように自分で型(type)を宣言して、任意のfunctionを実装することができる。今回、あるプログラムを実装している時に少しハマったので、経緯と対応策を記録しておく。

ありがち?な失敗

先述したようにGoでは任意の型を定義して、任意のfunctionを実装できる。タイトルにある自分で定義したprimitiveな型とは以下の様な宣言のこと。

1
type MyInt int

例えばこのMyIntに、10より大きければtrueを返すOver10()を実装し、以下の様に使いたいとする。

1
2
3
4
5
6
7
8
9
func main() {
    var i MyInt = 11

    if i.Over10() {
        println(i, " is over 10")
    } else {
        println(i, " is less than or equal 10")
    }
}

ここで、何も考えずに実装してしまうと、以下の様なコードを書いてしまう。

1
2
3
4
5
6
func (m MyInt)Over10() bool {
    if m > 10 {
        return true
    }
    return false
}

このfunctionをコンパイルすると、以下の様なエラーが発生する。

1
prog.go:6: invalid operation: i > 10 (mismatched types *MyInt and int)

対策

type MyIntはint型を基礎に持っているから普通に比較できるはずではと思ってしまうが、エラー文言にあるように、int型を基礎にしていても、intではなくMyInt型なのでintとして使うことはできない。ちょっと考えればわかることだ。

ではどうするか?今回の場合だと、対策としては2通りある。

  1. 比較相手である10MyInt型として利用する。
  2. 1.の逆で、i MyIntから値を取り出し、int型として扱う。

比較相手である10をMyInt型として利用する

1
2
3
4
5
6
7
func (i MyInt) Over10() bool {
  var ten MyInt = 10
  if i > ten {
      return true
  }
  return false
}

i MyIntから値を取り出し、int型として扱う

この場合はちょっと面倒になる。というのも、reflectパッケージを使って、変数の型の情報を取得する必要があるからだ。

1
2
3
4
5
6
7
8
9
10
11
12
func (i MyInt) Over10() bool {
  v := reflect.ValueOf(i)
  var num int
  if v.IsValid() {
      num = int(v.Int())
  }

  if num > 10 {
      return true
  }
  return false
}

まず、与えられた変数に値が入っているかどうかを確認し、入っていればInt()で取り出す。Int()で取り出した値はint64なので、intに変換しなければならない。

毎回この処理を実装するfunctionに書くのは面倒なので、以下の様なfunctionを用意しておくと良い。

1
2
3
4
5
6
7
func (i MyInt)ValueInt() (num int) {
  v := reflect.ValueOf(i)
  if v.IsValid() {
      num = int(v.Int())
  }
    return
}

primitiveな型を利用した型を定義するときには、少々面倒だが、上記のValueIntを実装することを忘れないようにしたい。

しかし、実際、みんなこんな風に実装しているのだろうか。さすがに面倒すぎる気がするなぁ。本当はもっと良い方法があるんだろうか?本音としては、このくらい、compileとかgo generate的な何かでで良い感じに対応していただきたいのだけど、そういうわけにはいかないのだろうか。