goで排他なフィールドとinvalidなzero valueではまった。 (短く言うならポインターにするべきところでポインターになっていなかったという話)
排他なフィールド
排他なフィールドを持っているstructと言うのはこういう形。
// T : type T int // const ( TX = T(0) TY = T(1) ) // A : type A struct { T T X X Y Y } // X : type X struct { } // Y : type Y struct { }
Aというstructは、Xに値が入る場合もあるし。Yに値が入る場合もある。 どちらに値が入るかはTの値で決まる。
以下の値は、Xに値が入るもの
main.A{
T: 0,
X: main.X{},
Y: main.Y{},
}
invalidなzero value
invalidなzero valueというのは初期状態で何かの操作を行うとエラーになるようなもののこと。
例えば、bson.Marshalするときに、bson.ObjectIdという型の値を使うことがあるのだけれど。
このbson.ObjectIdのzero valueはinvalid。適切なものにするには bson.NewObjectId() にしないとだめ。
例えばこういう型があったとして
// O : type O struct { ID bson.ObjectId `bson:"_id"` }
正常な値
正常に初期化した値はbson.Marshalが動く。
{
o := O{ID: bson.NewObjectId()}
fmt.Println("initialized", o)
b, err := bson.Marshal(o)
if err != nil {
log.Fatal("ng", err)
}
fmt.Println("ok", b)
}
大丈夫。
initialized {ObjectIdHex("594c9af90ccee09cd63ea023")}
ok [22 0 0 0 7 95 105 100 0 89 76 154 249 12 206 224 156 214 62 160 35 0]
不正な値
zero valueは不正なのでbson.Marshalはだめ。
{
// error is occured
var o O
fmt.Println("zero", o)
b, err := bson.Marshal(o)
if err != nil {
log.Fatal(err)
}
fmt.Println("ok", b)
}
だめ。
zero {ObjectIdHex("")}
2017/06/23 13:37:13 ObjectIDs must be exactly 12 bytes long (got 0)
排他なフィールドとinvalidなzero value
排他なフィールドとinvalidなzero valueと言うのは先ほどの2つが組み合わさった場合の話。
先程のAの型(XとYが排他なフィールド)が以下の様に変わった場合に
// A : type A struct { T T X X Y Y } // X : type X struct { ID bson.ObjectId `bson:"_id"` } // Y : type Y struct { V int `bson:"v"` }
Yの値だけが入った値(正常なもののつもり)をbson.Marshalしようとするとエラーになる。 理由は単純でA.Xのzero valueが不正な値なため。
a := A{T: TY, Y: Y{V: 10}} // 正常な値のつもり
// aの値
main.A{
T: 0,
X: main.X{
ID: ObjectIdHex(""), // 不正なzero value
},
Y: main.Y{
V: 10,
},
}
// ObjectIDs must be exactly 12 bytes long (got 0)
Xはポインターになっている必要がある。