sliceは内部にデータへのポインタを持っており、sliceそのものをポインタで渡すのは冗長であると言われる。他人のコードを見てもsliceのポインタを戻り値で返すケースはあまり見かけない。引数でsliceのポインタを受け取ることはあるけれど。
例としてmapから指定keyの値を[]stringとして返す関数を実装する場合。
func GetArray(mapObject map[string]interface{}, key string) ([]string, error) {
if mapObject[key] == nil {
// keyの値がnil、もしくは存在しない場合
return []string{}, nil
}
stringArray, ok := mapObject[key].([]string)
if ok {
// 値が[]stringだった場合
return stringArray, nil
} else {
// 値が[]stringでない場合
return []string{}, errors.New("Invalid value for key. " + key)
}
}
こうしてしまうと、
- keyの値がnull、もしくは、keyが存在しない場合
- keyの値が空配列だった場合
のどちらか区別ができなくなる。前者の場合はerrorを返すこと自体、おかしい気がする。
このようにsliceのポインタを返す方法もある。
func GetArray(mapObject map[string]interface{}, key string) (*[]string, error) {
if mapObject[key] == nil {
return nil, nil
}
stringArray, ok := mapObject[key].([]string)
if ok {
return &stringArray, nil
} else {
return nil, errors.New("Invalid value for key. " + key)
}
}
stringArrayPtr, err := GetArray(mapObject, "key1")
if err != nil {
// keyの値は[]stringではなかった
} else if stringArrayPtr == nil
// keyの値はnilだった
} else {
// keyの値は[]stringだった
// appendはやfor/rangeは、ポインタではできないので実体を指す必要がある
*stringArrayPtr = append(*stringArrayPtr, "new string")
for _, str := range *stringArrayPtr {
// 何かの処理
}
}
この方法なら、keyの値がnilだった時は、nilを返すようになる。しかし、最初に述べたようにsliceのポインタを返すのは冗長であるし、受け手側で実体を参照しないとappendやfor/rangeできないのは混乱をもたらしそうである。
そこでこの案はどうか?
func GetArray(mapObject map[string]interface{}, key string, stringArray *[]string) (bool, error) {
if mapObject[key] == nil {
return false, nil
}
stringArray2, ok := mapObject[key].([]string)
if ok {
*stringArray = stringArray2
return true, nil
} else {
return false, errors.New("Invalid value for key. " + key)
}
}
var stringArray []stringArray
has, err := GetArray(mapObject, "key1", &stringArray)
if err != nil {
// keyの値は[]stringではなかった
} else if has {
// keyの値は[]stringだった
} else {
// keyの値はnilだった
}
このようにすると、値がnilだった場合とエラーだった場合の区別ができ、かつ、sliceのポインタを返さなくていい。
ちなみにxormというO/Rマッパーライブラリから、この案を拝借しました。