Skip to content

golang tips

foolmacky edited this page May 12, 2023 · 10 revisions

■interface{}について

  • interface{}は実体は型情報と、データの実体or実体へのポインタの2つの値しか持っていない
  • 値が入るのか、ポインタが入るのかは、実体データの型によって異なる
    intならデータが入る。structは実体へのポインタが格納される
    なので、deep copyしてもあまり気にしなくても大丈夫
  • interface{}のポインタにはinterface{}としての機能が「無い」ため、デリファレンスする必要がある
package main
import log
type Person struct {
    Name string
}

func main() {
    p := Person{Name: "hoge"}

    iftest(p)
    iftestNull(nil)
    iftestNull()
}

//interface{}のタイプassertionはstructに関しても有効
func iftest(param interface{} {
    log.Println(param.(Person).Name)
}

func iftestNull(param interface{}) {
    if param == nil {
        log.Println("This is NULL")
    }
}

■インターフェース

  • 異なるパッケージのインターフェースも満たすことができる
  • インターフェースを満たしている関数のリスナーをポインタ型にすると、そのStructのポインター変数しかインターフェースを満たさない。実体の変数はインターフェースを満たさない。* リスナーを実体にしていれば、両方満たす。
package main
import log

type PSample struct {
    name string
}

type Sample struct {
    name string
}

type AllStruct interface {
    Exec()
}

func (psample *PSample) Exec() {
    log.Println(psample.name)
}
//func (psample PSample) Exec() {
    //log.Println(psample.name)
//}
//同じ関数定義を、ポインターと実体の両方で定義することはできない

func main() {
    psample := &Psample{name: "psample"}
    sample := Sample{name: "sample"}
    var allstructs = []AllStruct{psample, sample}

    for _, sam := range allstructs {
        sam.Exec()
    }

    psample2 := Psample{name: "psample2"}
//実体を一度ポインタ変数に代入すれば、インターフェースを満たすことができる
    var ppsample2 = &psample2
    sample3 := &Sample{name: "psample3"}
//実体の場合はポインタ変数にしても「当然」インターフェースを満たすことができる
    var allstrct2 = []AllStruct{psample, sample, ppsample2, sample3}
    for _, sam2 := range allstruct2 {
        sam2.Exec()
    }
}
  • structにinterfaceを埋め込む事もできる。その場合、structは埋め込まれたinterfaceによってインターフェースを満たしてしまい、関数が実装されていなくても、コンパイルできてしまう。実際に実行すると、ランタイムエラーが発生する。
  • それなら、と実装されていない関数を実装しているstructを追加で埋め込むと、interfaceとstructのどちらの関数を使うべきか判断できないため、ambiguousエラーでコンパイルできない。
  • なので、structを埋め込んでインターフェースを満たす形にするのが本来のやり方
  • structのメソッドは、structに紐付いているフィールド、メソッドしかメソッドのリスなオブジェクトから取得することはできない。
  • 埋め込みによってインターフェース的には透過的に満たしていたとしても、それはそのオブジェクトを「使っている」側から見た話であって、埋め込まれた側のstructから、埋め込まれているstructの属性が見える、ということではない。

■pointer関連

■expert GO

https://www.slideshare.net/takuyaueda967/go-77689475

■go modulesの周辺

正規バージョンではなく、特定のコミットを使いたい場合の指定について

  • 特定のコミット指定をgo.modに記入するのは面倒くさい。その場合はgo getでコミットhashタグを指定して取得するとgo.modを自動で更新してくれる
go get \<リポジトリ\>@\<hashタグ\>
  • versionタグを付与していないパッケージの場合 go.modの記載ver.は通常v0.0.0-TIMESTAMP-hashだが、途中までver.タグが付与されていると必ずしもそうではない。go getしてgoに書き換えてもらうのが良い。

go buildの周辺

  • 利用しているライブラリがリポジトリ移動した場合のエラー
module declared its path as < new repository>
   but was requires as < old repository>
  • 参照しているソース自体、タグを含めて新しいリポジトリに移動していたとしても、そのタグが保持しているgo.modに古いリポジトリが設定されているとこのエラーが出る
  • 使うバージョンをgo.modも新しいリポジトリを指しているものに更新する

zapはgo routine safeなのか?

  • こちらの記載にもあるように、基本的にログを出力する事においては、goroutine safeと思って良さそう
    https://stk132.hatenablog.com/entry/2017/10/01/035726
  • ただし、それはログ出力に限ってのことで、ログを出力している最中にログの設定そのものを変更したりする過程はそうではない。
  • なので、並列でログ出力をしながら、ある特定のgo routineでログの内容をチェックしたいからと言って、一時的にバッファーにログの出力先を切り替えて、もとに戻すといったことはsafeにはできない。しかも、偶然にできたとしても、並列で別のgo routineもログに出力しているわけだから、ある特定の出力の内容だけを得るということは至難の業だ。
  • そうしたことをするには、別のloggerを生成するか、zapをwrapして機能を拡張するしかないと思う

Clone this wiki locally