読者です 読者をやめる 読者になる 読者になる

Go で cannot assign to xxx のエラー

golang

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

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 への副作用

golang

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() が呼ばれる順番

golang

結論から言うと、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

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 を用意することのはどーなの?」
というのはありますね・・・。
ちょっといい例が思いつかなくて・・・。

struct に依存しない処理は function に切り出すのか、method に切り出すのか

golang

以前少し考えて自己解決して終わったんだけど、
ちょっとしたきっかけがあったのでアウトプットしてみる。

以下のような Person があって、
Hello(), Goodbye() には全く同じ「複雑な処理」がある場合・・・

type Person struct {
    name string
}

func (p *Person) Goodbye(input string) {
    //複雑な処理のつもり
    fmt.Println("common logic " + input)

    fmt.Println("goodbye " + p.name)
}

func (p *Person) Hello(input string) {
    //複雑な処理のつもり
    fmt.Println("common logic " + input)

    fmt.Println("hello " + p.name)
}



以前はこれを以下のように commonLogic() として method で切り出していた。

type Person struct {
    name string
}

func (p *Person) Goodbye(input string) {
    p.commonLogic(input)

    fmt.Println("goodbye " + p.name)
}

func (_ *Person) commonLogic(input string) {
    fmt.Println("common logic " + input)
}

func (p *Person) Hello(input string) {
    p.commonLogic(input)

    fmt.Println("hello " + p.name)
}



ここでポイントになるのが、
commonLogic() では receiver である Person を利用していない点。
commonLogic() は Person の field に紐づくわけではないが、
Person 内で完結する処理なので、
Person の method として切り出している。

自分は php, java をメインで利用していて、
struct = class のようなイメージがある。
そして、クラスのfieldに紐付かない重複処理であっても、
以下のようにクラスに属する private なメソッドとして切り出すことが多かった。
多かったというか、普通はこうすると思う。

<?php
class A {
    public function xxx() {
    }

    public function yyy() {
    }

    // 切り出した処理
    private function commonLogic() {
    }
}



なので、golang でも struct に属する package private な method として切り出していたが、
net/http/transport.go を読んだ時に、
こういった共通処理を function で切り出しているのを偶然見つけ・・・
https://github.com/golang/go/blob/release-branch.go1.6/src/net/http/transport.go#L345

golang では struct に紐付かない処理を切り出す場合は、
method よりも function で切り出した方がいいのかな?
と思ったので少し考えてみた。


重複処理を method に切り出した場合、struct からしか method を呼べない。
「その struct に関係する処理だから、その struct に実装する」
というのは問題ないと思う。

ただ、golang の場合
package private な method は package 内で実行できてしまう。
以下の Person は・・・

type Person struct {
    name string
}

func (_ *Person) commonLogic(input string) {
    fmt.Println("common logic " + input)
}



package 内であれば commonLogic() を実行できてしまう。

p := &Person{"pospome"}
p.commonLogic("my_input") //同じ package 内であれば、commonLogic() が呼べる

この commonLogic() は一見 Person に紐づいているが、
Person の値を利用することはない。
Person に関係ない処理が Person に紐づくのは違和感がある。

php, java の場合はクラス単位で private になるので、
同一 package 内であっても、commonLogic() は呼べない。
クラスの利用者は常にクラスに紐付いた振る舞いを呼ぶことになるので、
違和感なく利用できる。


golang は struct 単位の private がないので、
package 内ではこういった自然な振る舞いを提供できない。
「struct に紐付いていない振る舞いは struct に実装する必要がない」
というのは自然な考えのように思える。

function の面倒なところは名前が package 内でユニークになるという点。
function が増えすぎると、prefix, safix が多くなったり、
他の function と区別するために命名が冗長になるかもしれない。

ただ、function が増えて管理しづらいということは、
その package が大きすぎる可能性がある。
function や struct の性質によって package を分割することを考えた方がいいかもしれない。

また、function は値と振る舞いを一緒に持たないので、
function が増えて管理できないということは、
本来 struct, primitive type にすべきものを見落としている可能性がある。

例えば、以下の Person でいうと・・・

type Person struct {
    name string
}

func (p *Person) Goodbye(input string) {
    p.commonLogic(input)

    fmt.Println("goodbye " + p.name)
}

func (_ *Person) commonLogic(input string) {
    fmt.Println("common logic " + input)
}

func (p *Person) Hello(input string) {
    p.commonLogic(input)

    fmt.Println("hello " + p.name)
}



以下のように string の input を primitive type にして、
commonLogic() を実装した方がいいのかもしれない。

type Input string

func (i Input) commonLogic() {
    fmt.Println("common logic " + i)
}

type Person struct {
    name string
}

func (p *Person) Goodbye(i Input) {
    i.commonLogic()

    fmt.Println("goodbye " + p.name)
}

func (p *Person) Hello(input string) {
    i.commonLogic()

    fmt.Println("hello " + p.name)
}


https://github.com/golang/go/blob/release-branch.go1.6/src/net/http/transport.go#L345

逆に言うと、上記の checkTransportResend() のロジックは
引数になっている Request, persistConn のどちらかに対して実装すると
責務として不自然になってしまうのだと思う。
かといって、Request, persistConn を持ち、
checkTransportResend() を実装するような struct を作るまでもない、
もしくは、仕様上そのような概念が存在しないので、
function として切り出しているんだと思う。
http.checkTransportResend() というのが自然な文脈になる。
DDD でいうところのドメインサービス的なイメージかな。

ちなみに、struct に紐付かなくても、
以下のように特定の struct に対する固定値を実装する場合に method を利用するのは問題ないと思います。

type A struct {
}

func (_ *A) isXxx() bool {
    return true
}


結論

今回の内容は transport.go を見た時に、
他の標準パッケージやdockerのソースもサラッと見て、
こういった傾向があるのかなと思って考えたものですが、
全てのコードを調べたわけでもないので、
「function を利用する」と言い切れるものではないです。
*もし、private mehtod に切り出している処理があれば教えてください。

記事中の例では「共通処理を切り出すケース」について説明しましたが、
長いメソッドを切り出す際にも
function として切り出すのか、
method として切り出すのかを意識して書く必要があると思います。

皆さんはどうお考えでしょうか?

何か意見があれば、
コメント or twitter にメンションください。