Datastore/GO で datastore: flattening nested structs leads to a slice of slices: field xxx

以下のエラーが出た。

datastore: flattening nested structs leads to a slice of slices: field xxx


Datastore は以下のように struct をネストすることができるが、

type A struct {
    B []B
}

type B struct {
    Name string
}


以下のようにネストした struct (今回で言うとB)が slice を持つと、
このエラーになる。

type A struct {
    B []B
}

type B struct {
    Name string
    Score []int //これがダメ
}

Go で cannot assign to xxx のエラー

ググると色々情報があるが、
日本語の情報がないので書いておく。

https://play.golang.org/p/atdCt2Mken

これを実行すると「cannot assign to struct field users[0].Connected in map」のエラーが出る。

結論から言うと map が問題らしい。

go - Why do I get a "cannot assign" error when setting value to a struct as a value in a map? - Stack Overflow

ここに答えっぽいのが書いてあるけど、
正直良くわからなくて、
map はアドレス指定できないもので、
実行中にそれを指定して参照しようとすると、このエラーが出るっぽい。
https://golang.org/ref/spec#Address_operators
http://golang.jp/go_spec#Address_operators

以下の記事でも map の値が移動するような記述がある
http://wazanova.jp/items/856

新しいbucket配列のメモリが利用可能になると、古いbucket配列のkey/valueペアは新しいbucket配列に移動("evacuated" = 避難)する。key/valueペアが追加もしくは削除されるタイミングで、移動は起きる。古いbucketで同じ場所にあったkey/valueペアは、別々の新しいbucketに移る可能性がある。key/valueペアを均等に配置しようというアルゴリズムが働くからである。


最初に提示した以下の場合は・・・
https://play.golang.org/p/atdCt2Mken

以下のようにするとエラーにならない。
https://play.golang.org/p/8OEZ-hKN9G

map で key 指定した struct ごと変更してあげれば大丈夫っぽい。
map の参照がアドレス指定できない場合はエラーになるようなので、
今回のように対象が struct 以外の場合でも(string などでも)エラーになる。

twitter にてポインタにするとエラーにならないとのメッセージをいただきました。

たしかにエラーにならないですね。
自分は基本ポインタ使うので、遭遇しなかったのかな。
https://play.golang.org/p/3aflz7y1ku


これ詳しく分かる人、コメント or twitter で教えていただけると助かります・・・。

Go のブランク識別子を利用した import による pakcage への副作用

ORMを調べていると以下のような import を見かけた。

import _ "github.com/go-sql-driver/mysql"

調べてみると、
これはブランク識別子を利用した import で、
import 対象のパッケージを初期化するためのものらしい。

つまり、
init() や package value として宣言している変数を初期化するために
わざわざ import していることになる。

ただ、なぜ初期化が必要なのかが分からなかった。

以下で書いたようにわざわざブランク識別子で import しなくても、
そのパッケージに依存するパッケージが import してくれるはずなのでは?
pospome.hatenablog.com


調べてみると、
DBのドライバを差し替えるために
ブランク識別子による import が必要であることがわかった。

golang には database/sql というDBを扱うパッケージがある。
これはSQLの発行やトランザクションの実行などを提供するもので、
DB操作はこのパッケージだけで完結する。

そして、DBごとの差異(接続方法の違いなど)はドライバという形で、
別途用意されている。
https://github.com/golang/go/wiki/SQLDrivers#drivers

なので、利用するDBによって、利用者がドライバを import する必要がある。

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql" //mysqlのドライバを利用
)


上記の go-sql-driver/mysql を import すると以下が実行され、
database/sql にドライバがセットされる。
https://github.com/go-sql-driver/mysql/blob/master/driver.go#L182

ドライバがセットされないと、以下でエラーになる。
https://github.com/golang/go/blob/master/src/database/sql/sql.go#L572

Oracle, Postgres を利用したい場合はそれ用のドライバを import すればいい。

ORMは database/sql をラップして
クエリビルダやstructへのマッピングなどの便利機能を提供しているので、
ドライバに影響を受けることはない。
ドライバが Oracle だろーが、Postgres だろーが問題なく動作する。

ただ、ドライバが提供するのは SQL を実行するために最低限必要な実装だけなので、
ORM側で各DBの違いを吸収する必要がなくなるわけではない。

例えば、以下のようなクエリビルダを提供する場合・・・・

orm.Find().Table("tb_test").Limit(10)

MySQL であれば以下のSQLになるが・・・

SELECT * FROM tb_test LIMIT 10;

Oracle であれば以下のSQLになる。

SELECT * FROM tb_test WHERE ROWNUM <= 10;

こういったSQLの方言はORM側で吸収する。

xorm でも各DBごとに実装が存在する。
https://github.com/go-xorm/xorm/blob/master/mssql_dialect.go
https://github.com/go-xorm/xorm/blob/master/mysql_dialect.go
https://github.com/go-xorm/xorm/blob/master/oracle_dialect.go

Go の init() が呼ばれる順番

結論から言うと、import される順番に依存する。

以下のような main での import を例にすると、
first の init() が呼ばれた後に
second の init() が呼ばれる。

package main

import (
    myapp/first //最初に呼ばれる
    myapp/second //次にこれが呼ばれる
)

func main() {
}



仮に first の中で second が import されていたとすると、
first package で second の init() が呼ばれるので、
second の init() が最初に呼ばれる。

例えば、a, b という package があって・・・・

package a

func init() {
    if b.Value == 100 {
        //何かしらの処理
    }
}
package b

var Value int

func init() {
    b.Value = 100
}

a の init() で b の init() 内で初期化される変数を参照していても、
a が b を import している限り、b の init() が先に実行されることが保証されている。

これによって、
「このパッケージの init() が最初に実行されないと上手く初期化されない」
ってことを心配する必要はなくなる。
init() 同士に依存関係があっても特に意識しなくていい。

ちなみに、同一パッケージで複数の init() を定義することもできるので、
その場合の実行順もサラッと調べた。
http://qiita.com/astronoka/items/aa2f271d280863cedf5e
http://stackoverflow.com/questions/24790175/when-is-the-init-function-in-go-golang-run

ただ、これは意識する必要ないと思っている。

なぜかというと、同一パッケージに複数に init() を定義して、
それらが依存しているのって、
パッケージの設計(粒度)がおかしい気がするので、
見直したほうがいいかと。

こちらも import に関する記事です。
pospome.hatenablog.com

struct に static method は必要か?

golang を触りたての頃、struct に static method を実装したいと思った。

なぜかというと、
1つの package に function をずらーっと定義すると、
なんだか冗長な命名になってしまうから。

util.UserXxx()
util.TeamXxx()
util.EventXxx()



package を切って、以下のようにしても良かったのだが、
package 名が重複して import の時にエイリアスを付けるのが面倒だった。

user.Xxx()
team.Xxx()
event.Xxx()


理想としては、以下のようにしたかった。

util.User.Xxx()
util.Team.Xxx()
util.Event.Xxx()



今思うと、
これは以下のようなクラス単位で static method を生やすイメージで、
クラス単位で管理する思想に引きづられているように思える。

<?php
class User {
    public static function Xxx() {

    }
    public static function Yyy() {

    }
}

class Team {
    public static function Xxx() {

    }
    public static function Yyy() {

    }
}


そして、それに近いことができることを知って、
このように static method を書いてた時期もあった。
http://qiita.com/Jxck_/items/547f1c16669f91cc9c29


ただ、最近はこういった書き方をしなくなった。


理由としては以下。

1.golang の言語仕様に沿った方が良いと思ったから

そもそも golang では struct に static method を持たせることができないので、
無理やり持たせるのってそもそもおかしいのでは? と思った。
golang の struct と他の言語のクラスを同じようなイメージで利用するのは正しくない気がする。

2.「package の中の package」のような妙な領域が発生するから

以下のコードを見ると、util package の中に User, Team, Event という領域が存在するように思える。
こうなると、1つの package が持てる責務が大きくなってしまう可能性がある。

util.User.Xxx()
util.Team.Xxx()
util.Event.Xxx()

3.method 内で method を呼ぶときの呼び方に違和感があるから。

以下のように util.User から yyy() を呼ぶこともできるし、

func (_ User) Xxx() {
    return util.User.yyy()
}

func (_ User) yyy() {

}

以下のように receiver からも呼ぶことができる。

func (u User) Xxx() {
    return u.yyy()
}

func (u User) yyy() {

}



実体は struct なので、こういった書き方ができてしまう。
自分はここに違和感を感じる。
利用する側が static method っぽい書き方になるだけで、
struct 本来の使い方を見失っているように思える。


今はどうしてるのか?

ということで、最初に以下が理想だと言ったが、

util.User.Xxx()
util.Team.Xxx()
util.Event.Xxx()

現在は結局以下のように package を分けて定義したり・・・

user.Xxx()
team.Xxx()
event.Xxx()



prefix, safix を付けるようにしている。

util.UserXxx()
util.TeamXxx()
util.EventXxx()



package を分ける場合、
user, team, event という package が import で重複したら、
エイリアスを付ける。
面倒だけど・・・。

このように function を定義していくと、
package 内に User, Team, Event という「package の中の package」のような妙な領域は発生しないので、
1つの package の責務が肥大化する可能性が低くなる。
その package に属するのが自然になるように function として定義するのが一番だと思う。

仮に package 内に function が増えてきてゴチャゴチャしてきたら、
それは package を分割した方がいいというサインかもしれない。

ちなみに、標準 package とDockerのコードをサラッと grep したけど、
struct に static method を持たせるような実装は見当たらなかった。
*存在したら教えてください

今まで自分が触ってきた「クラスベースの private が当たり前だった言語」と同じように考えると、
struct に static method を持たせたくなるが、
struct に static method を持たせることはできないし、
golang は package private が基本になる。
package ベースで考えることで、
本来あるべき golang のコードに近づくかもしれないですね。

ちなみに、今回は util という package を例にしましたが、
「そもそも何でも突っ込める感じのする util みたいな package を用意することのはどーなの?」
というのはありますね・・・。
ちょっといい例が思いつかなくて・・・。