すてにゃん氏の技術ぶろぐ

技術っぽいこと書きます

The Go Programming Language 6章まで読みました

The Go Programming Language (Addison-Wesley Professional Computing Series)

The Go Programming Language (Addison-Wesley Professional Computing Series)


日本語訳もあるみたいですが私は英語で読みました。このまま読み進む前に雑にメモっといたほうがよいかなということで書いてみました。一部のコードは本の中にあるのを引用したりちょっとコメント書いたりしています。

  • main関数あるのC言語っぽい(こなみかん)
  • gofmt, goimports など使ってコードをフォーマットしておくと嬉しそう
  • whileループはなく、forを代わりに使う
    • C言語とかでもfor(;;)とやれば良いしwhileなくても特に困らなそう
  • 1文字目が大文字の場合exportされる
  • Golangにはuninitialized variableなんてものはなく、デフォルトでzero valueになる
  • pointerとかnewとかあって一見C言語っぽいけどGCがあって明示的にfreeとかする必要はない
  • tuple assignmentで複数の変数同時にassignできてべんり
// tuple assignment
x, y = y, x
  • packageをimportしたにもかかわらず利用しなかったらエラーになる
  • Golangではnegativeにならない数値でもsigned intを使うことが多い、例えばlen関数もsigned intを返す。
    • 本の例だと例えばfor文で逆からiterateする場合、lenの返す値がもしunsigned intなら0のあと-1にならず逆にmaximumな値になり実行時エラーになるので、signed intを使う
  • 多くのconstantは1つの型に縛られてるわけではなく、型変換なしで色々なコードで利用できる
var x float32 = math.Pi
var y float64 = math.Pi
var z complex128 = math.Pi
  • constant generator iotaを使うと便利に複数の関連する変数の値を指定できる、iota自体の値は0からはじまり、1ずつあがっていく
const (
    _ = 1 << (10 * iota)
    KiB // 1024
    MiB // 1048576
    GiB // 1073741824
    // ...
)
  • arrayやstructはfixed sizeで、sliceやmapはdynamic
  • Golangでarray自体はあまり使われることはないらしい
  • array同士を == などで比較することが可能(配列のサイズと中身が同じならtrue)
// 配列のサイズを書かずに...とすることができ、その場合は与えられた要素の数になる
q := [...]int{1, 2, 3}

// この場合keyが99の要素の値が-1で、それ以前の0~98の値はzero-value、要素数は100になる
r := [...]int{99: -1}
  • sliceにはunderlying arrayがあり、複数のsliceが同じarrayを参照することが可能
  • arrayみたいに==で中身を比較なんてできなくて、自分でそういうの書く必要がある
    • nilと比較することは可能、nil sliceとはunderlying arrayのないsliceのこと
    • nilじゃないけどemptyなsliceもあるので、そういう場合は len(s) == 0 でチェックする必要がある
  • Golangにはset型はないけど、mapのkeyはdistinctなのでmapをsetとして利用できる
  • struct内のfieldも全部comparableなら、struct自体もcomparableなので == で比較できる上、mapのkeyもcomparableならOKなのでkeyとしても利用できる
  • 関数の定義の仕方いろいろ
// int型のxとyを受け取って、intを返す
func add(x int, y int) int   { return x + y }

// xとyの型は同じなのでOK、return valueに名前をつけることによって return とするだけで暗示的に返される
func sub(x, y int) (z int)   { z = x - y; return }

// 使わない変数などに関しては _ を利用できる
func first(x int, _ int) int { return x }

// 型のみ書くのもOK
func zero(int, int) int      { return 0 }
  • Golangでは関数のパラメータはpassed by valueなので元の値は影響されずコピーが使われる
    • pointerやsliceやfunctionなどの場合はcallerに影響はあるかも
  • 別の言語で実装がある場合はfunction declarationのみに
  • Golangではエラーは基本ifなどのブロックでfailureをチェックし、successの場合はそのままindentせずに続ける感じ
resp, err := http.Get(url)
if err != nil {
    return nil, err
}
// successの場合はこの下に続く
  • fmt.Errorfを使って、元のエラー文の前にどんどん内容を付け加えつつmain関数まで戻っていく、これによってエラーの内容がより詳細に出力される
  • このようにエラー内容がどんどんchainされてくので基本的に大文字や改行は使わない方向でやっていく
  • 関数を値に代入したり、他の関数に渡したり関数から返したりすることができる
  • 無名関数にも対応している
  • variadic functionは...を使えば簡単に定義できる(暗黙的にarrayが確保され、sliceとして値が関数に渡される)
    • 型としてinterface{}を指定すると、どんなものでも受け取るみたいにできる
  • deferは何度でも呼ぶことができて、スタックされていくので逆から順番に呼ばれる
  • 例えばいちいち何かをOpenした直後にCloseを呼ぶとかしなくてもdeferで呼べば最後に必ず呼ばれる
  • panicが起きた際、deferされたものが順番に実行され、ログメッセージとともにプログラムがクラッシュする
  • panicを明示的に呼び出すこともできる、呼ぶとしたら絶対に起こり得ないことが起きる場合に
    • 大抵の起こりうるエラーの場合はpanicじゃなくてちゃんとerrorを返すようにするべき
  • panic中に実行されてるdeferされた関数内でrecoverが呼ばれたら、panicを止めてpanicの値を返す
    • それ以外の場合recover呼んでもnilが返されるのみ
  • 他のpackageのpanicからはrecoverしないほうがよい
    • recoverされる前提のもののみrecoverするように(あまりない)
  • オブジェクト指向、色々あるけど、Golangの場合はobjectとはmethodを持ったvalueやvariableのこと
  • methodの定義はfunctionと大体同じで、method名の前にreceiverを書くだけ
    • Golangにはthisとかselfとかはなくて、receiverには関数のパラメータと同じように名前をつけれる
  • pointerやinterface以外のものにmethodを定義できる
  • receiverにnilも使うことが可能
  • struct内に他のstructをembedすることができる
  • Golangで名前のvisibilityはcapitalizationのみに依存していて、structのfieldや何かのmethodでも同じ

こんな感じですね。ぱっと見C言語っぽいと思っていたけど、色々ともっと便利にかけたり、明示的にfreeとかしなくてよかったり、panic, recover, deferあたりの概念だったりでC言語とはやっぱり全然違うっぽいという印象ですね。C++とも全然違いますね。もっとサッとかけて良さそうですね。structとかmethod書きまくったりしてごちゃっとならないように気をつけないといけなさそうな雰囲気がありそう?panic周りだったりpointerだったりをちょっと復習したい気持ちがありますね。7章以降も読んだらまたメモります。