Open19

新規プロジェクトで得たgolangの知見

Hirotsugu KawasakiHirotsugu Kawasaki

パッケージ管理周りの情報が錯綜としている。
以下の記事が詳しい。
https://zenn.dev/spiegel/articles/20210223-go-module-aware-mode

個人的に注意すべきは

  • envは別にいじらなくても大丈夫
  • go getは使わない。大体、go mod tidyで事足りる
  • ソースではつかはないがインストールしたい開発ツールとかはgo installを使う。この時にバージョン指定は必須。
  • マルチモジュールだとvscodeの拡張機能がうまく動かないことがあるので、go.modがあるフォルダを開くと動く。
Hirotsugu KawasakiHirotsugu Kawasaki

golangはクラスがないし、パッケージレベルでのアクセス制御がないのでどのメンバを公開して、非公開にすればいいのかはじめ分からなかった。

これまではクラスのインターフェースになるメンバだけ公開して、内部の実装は隠していた。
ただこれと考え方は一緒で、パッケージのインターフェースになるメンバだけ公開すればよかった。

なんで混乱したかというと、アーキテクチャとかよく分からずに全部同じパッケージに突っ込んでいたから。
これまでフレームワークに乗っかっていただけなので、フレームワークなしのgolangを使い始めて、パッケージの切り方がわからなかった。

おかげでDDDとかレイヤードアーキテクチャについて調べるようになり、パッケージ間の依存についての知見が増えた。ほんの触りしか理解できてないが

Hirotsugu KawasakiHirotsugu Kawasaki

言語仕様がシンプルで冗長だが何書いてあるかはわかりやすいし、標準ライブラリとかもそんな分量ないのでAPI仕様書とソース読めば大体解決する。

Hirotsugu KawasakiHirotsugu Kawasaki

ローカル環境に依存しないでいいようにDocker + Remote Container(VSCode)を使って環境を構築した。
また、いちいちビルドしなくていいようにライブリロードライブラリのairを使っている。

素の環境じゃないのでデバッガ(delve)の設定方法がわからず、printデバッグを強いられている...
誰か助けて

Hirotsugu KawasakiHirotsugu Kawasaki

ロギングのライブラリがいくつかあるが、logrusとzapのどちらかにしようと思っている。
logrusは枯れていて安定している。現在メンテナンスモードでもう新規機能の開発はしないみたい。
zapはlogrusより新しくて早いみたい。
ログをltsv形式で出力したいがどちらも、デフォルトでは対応してないみたいなので自分でカスタムしないといけないが、どうやらlogrusの根本的な問題でltsvで出力するとログのカラムの順番が常に同じとは限らなくなるみたいで微妙。。
zapもいけるみたいだけど、logrusよりカスタムが複雑。。
多分zap使うけど面倒くさいから公式でサポートして欲しいという気持ち

Hirotsugu KawasakiHirotsugu Kawasaki

goはクラスはないがインターフェースはある。
インターフェースはrubyと同じでダックタイピング。

クラスがないとかダックタイピングにしてるとか、goの思想みたいなのを感じるがオブジェクト指向に造詣が深くないので理由はちゃんと理解してない。

Hirotsugu KawasakiHirotsugu Kawasaki

例外がなくて、エラーハンドリングが特殊。
goの関数は多値を返せるので、その時にエラーを返してくる。

hoge, err := fuga()
if err != nil {
   // エラー処理
}

みたいな処理がかなり出てくる。

Hirotsugu KawasakiHirotsugu Kawasaki

githubのリポジトリをモジュールとしてインストールできるが、プライベートリポジトリをインストールしてくる時は少し面倒。

Hirotsugu KawasakiHirotsugu Kawasaki

以下の型の変数はnilを値に取ることができる。

  • ポインタ型
  • 関数型
  • スライス
  • チャネル
  • マップ
  • インターフェース型

基本的にnilはポインタがないことを意味している。(関数型もスライスも関数やスライスの実態があるアドレスへのポインタを保持している)
ただ、この中でインターフェース型だけ特殊で、そのせいでハマりやすい。

インターフェース型自体は、振る舞いだけ定義してあって、内部にどんなデータを持つのかは定義していないので、コンパイル時にはどんな型のデータが入ってくるかわからず、動的に決まる。
そして、インターフェース型の値は動的な型と動的な値という二つの構成要素を持つ。

続く

Hirotsugu KawasakiHirotsugu Kawasaki

ライブラリ等のインターフェースや構造体に新しいメソッドを生やしたい場合は、新しいインターフェースや構造体を作成し、委譲をつかうことで実現できる。

Hirotsugu KawasakiHirotsugu Kawasaki

timeパッケージでは日時フォーマットの指定の仕方が独特。
2006/01/02 15:04:05のような形式で指定する。
この時にちゃんと年は2006年で月は1月にしないとおかしくなる。2007年とかじゃだめ。

自分は時間を15時ではなく3時にしていて、日時が12時間ずれて気づくのに結構時間がかかった。
最初ロケールのせいかと疑った。ロケールも環境によってはAsia/Tokyoが設定されていなことがあるらしく、気をつけるポイント。
https://www.kwbtblog.com/entry/2020/05/15/190332

Hirotsugu KawasakiHirotsugu Kawasaki

レイヤードアーキテクチャを使っていて、contextを上のレイヤから下のレイヤに渡すとき、キーをどのレイヤに置けばいいのか迷った。
レイヤを横断的に使うものはルートディレクトリに新しくフォルダを切って、そこに入れるようにした。
ロガーもここに入れることにした。

Hirotsugu KawasakiHirotsugu Kawasaki

cのライブラリを使っていて、ビルドすると自動的に動的リンクになる。
ただ、ビルドした環境のglibcのバージョンが、実行する環境のglibcのバージョンより新しい場合、以下のようなエラーが出る。

./main: /lib64/libm.so.6: version `GLIBC_2.29' not found (required by ./main)

ググるとビルド時に CGO_ENABLED=0 を付ければいいみたいな記事が出るが、それはcライブラリを使っていない場合だけで、使っている場合ビルドできない。(当たり前)

Hirotsugu KawasakiHirotsugu Kawasaki

sqlboilerでテーブルからモデルを生成するときに、複数形でないが最後にsがつくテーブルのモデルがsが削除されてしまう。
例えば、テーブル名がflops(=Floating-point Operations Per Secondの略)の場合、生成されるモデルはflopになる。
この場合tomlに以下のような設定を書く。

[aliases.tables.flops]
up_plural     = "Flopses"
up_singular   = "Flops"
down_plural   = "flopses"
down_singular = "flops"

https://github.com/volatiletech/sqlboiler#aliases

REAMEに書かれてあるが、どう設定すればいいのか分かりづらく結構時間を食った。
PR出してみようかな。