Open21

Go言語のナレッジ

Goのエラーハンドリングが丁寧なんは
try~catch~finally がないからなんか!それもあってか、関数の返り値でエラー型が返ってきたて、
if err != nil でエラーハンドリングしてたのね。
それもあって皆んなGoのエラーハンドリングについて、やたらと議論してたのね。なるほど。

コンパイルとは (復習)

この「実行できるような形」は、バイナリーコードになった実行ファイルである。

string --> int64 に変換する方法

    convertedStrInt64, _ := strconv.ParseInt(strInt64, 10, 64)

このエラーハンドリング例、色んなGoのエラーハンドリングの種類詰まってていい例

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
)

type DownloadError struct {
  StatusCode int
}
func (e *DownloadError) Error() string {
    return "httpd error!"
}

type SaveError struct {
  Filename, Message string
}
func (e *SaveError) Error() string {
    return e.Message
}

func main() {
  retry_count := 0
  for {
    err := DownloadToFile("http://example.com/hogehoge.html", "/tmp/hoge.html")
    if err == nil {
      fmt.Println("download success!")
      os.Exit(0)
    }
    // errインスタンスの型判別
    switch e := err.(type) {
      // ダウンロードエラー
      case *DownloadError:
        fmt.Printf("ダウンロードエラー %s [retry=%d, code=%d]\n", e.Error(), retry_count, e.StatusCode)
        retry_count++
        if retry_count > 3 {
          fmt.Printf("retry count over\n")
          os.Exit(1)
        }
      case *SaveError:
          fmt.Printf("保存エラー: %s [filename=%s]\n", e.Message, e.Filename)
          os.Exit(2)
      default:
        fmt.Printf("その他のエラー: %s\n", err)
        os.Exit(3)
    }
  }
}

func DownloadToFile(url, filename string) error {
  // ファイルダウンロード
  resp, err := http.Get(url)
  if err != nil {
    return err
  }
  defer resp.Body.Close()
  // 200以外のレスポンスはダウンロードエラーとする
  if resp.StatusCode != http.StatusOK {
    return &DownloadError{StatusCode: resp.StatusCode}
  }
  data, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    return err
  }
  // ファイルへ保存
  err = ioutil.WriteFile(filename, data, os.ModePerm)
  if err != nil {
    return &SaveError{Message: err.Error(), Filename: filename}
  }
  return nil
}

Goはstringでも比較演算できる。

		if startMonth > endMonth {
			fmt.Printf("start is bigger \n")
		} else if startMonth < endMonth {
			fmt.Printf("end is bigger \n")
		} else {
			fmt.Printf("N/A \n")
		}

ふーん、go-swagger(BE)とopenapi-generator(FE)使えば、サクッと楽に作れんねやー

ちなみにgo-swagger使うと本当は凄く楽に作れるのですが(ついでにフロントはopenapi-generator)、今回はClean Architectureを理解するのが主目的なので、サーバーは手書きでopenapiのyamlも1から自作しました。

https://zenn.dev/ulwlu/scraps/170d2d2412daf7

確かに過ぎる!!

気付けよう。

こんな感じで一々内部呼び出しの結果をエラーチェックして返していたりするが、

func bar() (string, error) {
    v, err := foo()
    if err != nil {
        return "", err
    }

    return v, nil
}

普通にこう書けばいいんじゃないの? という話。

func bar() (string, error) {
    return foo()
}

JSでいうobjectやね。

map とは

rubyでいうと、ハッシュ(連想配列)に該当するものです。
キーと値がセットになっているものです。

こーゆーやつ

    m := map[string]int{"udon": 500, "ra-men": 800, "soba": 700}
    fmt.Println(m) // => map[ra-men:800 soba:700 udon:500]

なるほど。よくわからん。

Mapはスレッドセーフじゃないので、複数スレッドからアクセスされる場合には排他制御する必要がある。各々使う場所でやるよりも、こんな感じでアクセス関数群を用意しておいた方が良いよね、という話。

Goの正規表現

正規表現パターンの生成にはコストがかかるため、実行中に動的に生成するよりは、可能な限り初期化時に生成しましょう。具体的には、packageのvarかinitの初期化の際にパターンの生成を済ませてしまうということです。その際、正規表現パターンの生成にはregexp.MustCompileを使います。

https://itnext.io/10-features-of-go-that-set-it-apart-from-other-languages-89337e5ee551

へえー、Goやと起動むっちゃ早いんやー。
逆にPythonとかJSはそんな遅いんやー

App Engine can spin up an instance of your Go program in the blink of an eye. The same experience in Python or Node usually results in 3–5 second wait (or longer) as the required virtual environment is also spun up alongside the new instance.

なるほど!
だからポインターを渡すのか!この説明文でポインターを渡す意味がやっとさらに一段階あがった気する!

This means that any changes made to the object in the receiving function are reflected in original object.
In Go, structs and primitives are by default passed by value, with the option of passing a pointer , through the use of the asterisk operator:

// pass by value
func MakeNewFoo(f Foo) (Foo, error) {
   f.Field1 = "New val"
   f.Field2 = f.Field2 + 1
   return f, nil
}
The above function receives a copy of Foo and returns a new Foo object.
// pass by reference
func MutateFoo(f *Foo) error {
   f.Field1 = "New val"
   f.Field2 = 2
   return nil
}

The above function receives a pointer to Foo and mutates the original object.

なるほど。call-by-value は早くて、メモリフレンドリーで無駄をなくす最強&最高な機能とφ(・ω・*)フムフム...

What’s more, call-by-value significantly reduces the garbage collector’s job, meaning faster and more memory-efficient applications.

へえー、goは関数で関数返せんや。他の言語でも返せんのあるんかな?
あんま見たことない気する。あんま利用用途が思い浮かんでへんけど、片隅においとこー。
使ってみたいなー。will be nothing surprising tho.

func StartTimer (name string) func(){
    t := time.Now()
    log.Println(name, "started")
    return func() {
        d := time.Now().Sub(t)
        log.Println(name, "took", d)
    }
}
func RunTimer() {
    stop := StartTimer("My timer")
    defer stop()
    time.Sleep(1 * time.Second)
}

The above is an example of closure. The ‘StartTimer’ function returns a new function, which through closure has access to the ‘t’ value, set during its birth scope. This function can then compare the current time, to the value of ‘t’, thereby creating a useful timer. Thanks Mat Ryer for this example.

へえー、そうなんや。インターフェースがGoで唯一の
抽象なんや

interfaces are the only abstract type in Go.

Genericとは

They were designed to extend Java's type system to allow "a type or method to operate on objects of various types while providing compile-time type safety"

Effective Go

I see. Sounds effective. Should follow this as much as I can.

Interface names

By convention, one-method interfaces are named by the method name plus an -er suffix or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc

作成者以外のコメントは許可されていません