2022/01/08


技術評論社様、執筆者の皆様、献本ありがとうございました。

エキスパートたちのGo言語 一流のコードから応用力を学ぶ Software Design plus エキスパートたちのGo言語 一流のコードから応用力を学ぶ Software Design plus
上田 拓也, 青木 太郎, 石山 将来, 伊藤 雄貴, 生沼 一公, 鎌田 健史, 上川 慶, 狩野 達也, 五嶋 壮晃, 杉田 寿憲, 田村 弘, 十枝内 直樹, 主森 理, 福岡 秀一郎, 三木 英斗, 森 健太, 森國 泰平, 森本 望, 山下 慶将, 渡辺 雄也
技術評論社 Kindle版 / ¥3,212 (2021年12月27日)
 
発送可能時間:

本誌のサブタイトルは

入門書では得られないノウハウはコードにある

です。

ちなみに筆者も似た様な書籍「みんなの Go 言語」を共著で執筆しましたが、本書はこちらの拡充版とも言えるでしょう。

改訂2版 みんなのGo言語 改訂2版 みんなのGo言語
松木 雅幸, mattn, 藤原 俊一郎, 中島 大一, 上田 拓也, 牧 大輔, 鈴木 健太
技術評論社 Kindle版 / ¥2,350 (2019年08月01日)
 
発送可能時間:

「エキスパートたちのGo言語」は20名もの共著で色々なノウハウが沢山掲載されています。「みんなの Go 言語」は7名の共著で個々の特色を色濃く出せたと思いますので、どちらも良い作品だと思います。

昨今の Go 言語は、日々ライブラリが更新され、新しいライブラリが生まれ、トレンドが更新され、古いライブラリが使われなくなるという、細胞が角質となり剥がれ落ちまた新しい細胞が生まれる様な循環を繰り返しています。

その様な中でこういった作品を出すという事は、言ってみればリスキーな事になり得ますし、「みんなの Go 言語」を書いた際にも感じていました。しかしこういった情報をきちんと纏まった形で公開する事は非常に大切で、体系立てて読める事はとても素晴らしい事だと再認識しました。

本書の読者レベルで言うと、Go の基本的なコードが読み書き出来て、ライブラリの選定もある程度はできる、中級者くらいかなと思います。前述の様に最近の Go はツールやライブラリの入れ替わりが速い状況ですので、開発者にキャッチアップの能力が必要になります。

  • ツールやライブラリのトレンド
  • CI/CD 周りの扱い方
  • セキュリティfixのキャッチアップ
  • AWS/GCP などクラウドのトレンド

これら何れも Go 言語に限った話ではないのですが、Go 言語の場合はクラウドで使われる事が多く、自動化もしやすい為、特にツール類や CI/CD 周りに気を使わないといけない事が多いです。本書ではそういった内容を「2021 年の Go 言語」としてうまく書かれているなと思いました。

例えば通常の言語ではあまり気にされないかもしれない「リトライ処理」、クラウドで使われる事を意識してずいぶん慎重に扱われます。バックオフを意識したライブラリだけでも沢山公開されています。本書の中でもリトライ処理だけで10ページも割かれています。context の使い方やリトライ処理といった、基本的な使い方だけでなく、cgo を扱う際のノウハウ、GitHub Actions を扱う上でのノウハウ、ウェブアプリを作る際に必要な JWT/OAuth といった認証周り、WebAssembly など、色々なカテゴリで書かれています。

興味のある方は、ぜひ一度、目次とその章で割かれているページ数を参照されるのが良いと思います。1点、牽引が少ない気はしました。もしかしたら改訂版などで改良されるかもしれないですね。

本書の内容に関して、全て知ってはいなくても良いと思いますが、読んで「あー、あれね」くらいで理解が出来れば Go 言語のエキスパートと呼ばれても良いのではないでしょうか。Go 言語を使ってお仕事をされておられる方であれば、買って間違いなしの書籍だと思います。

Posted at by



2021/12/24


Go の http パッケージの Request.Body はこれまで最大バイト数を指定できなかったので、巨大なファイルをアップロードするといった DoS 攻撃を心配するのであれば、各ハンドラの中で独自でサイズ制限しながら読み込む必要がありました。Go 1.18 から http.MaxBytesHandler が入ったので簡単にサイズ制限をする事ができる様になりました。

MaxBytesHandler は http.Handler として提供されるので、例えば labstack/echo であれば以下の様に、各ハンドラを直接修正する事なく、簡単に導入して使う事ができます。

package main

import (
    "encoding/json"
    "fmt"
    "net/http"

    "github.com/labstack/echo/v4"
)

func middleware(next http.Handler) http.Handler {
    // MaxBytesHandler で wrap する
    return http.MaxBytesHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        next.ServeHTTP(w, r)
    }), 4096)
}

func main() {
    e := echo.New()

    // ココ
    e.Use(echo.WrapMiddleware(middleware))

    e.GET("/"func(c echo.Context) error {
        return c.String(http.StatusOK, "")
    })

    e.POST("/"func(c echo.Context) error {
        var v interface{}
        err := json.NewDecoder(c.Request().Body).Decode(&v)
        if err != nil {
            return err
        }
        fmt.Println(v)
        return c.String(http.StatusOK, "")
    })
    e.Logger.Fatal(e.Start(":8989"))
}

試しに大きな JSON を POST すると、500 エラーが返りました。イイカンジです。

$ curl -i -X POST -H "Content-Type: application/json" -d @bar.json http://127.0.0.1:8989/
HTTP/1.1 100 Continue

HTTP/1.1 500 Internal Server Error
Content-Type: application/json; charset=UTF-8
Date: Thu, 23 Dec 2021 15:55:30 GMT
Content-Length: 36
Connection: close

{"message":"Internal Server Error"}
Posted at by



2021/09/24


Go のテンプレートエンジンは、一般的なテンプレートエンジンの記法と気色が異なり、独特の文法で記述するのですが、ループ制御構文に関してはお世辞にも満足できる物ではありませんでした。それは continue や break が無いというのが理由です。continue や break が無かったので、無駄に if をネストして条件にあった値を出力しなければならず、必然的に無駄な if のネストが起きていました。

■ビーフストロガノフ
  2011年 受賞
  2019年 受賞
■カレーライス
  2012年 受賞
■満漢全席
  2015年 受賞
  2019年 受賞

例えば上記の様な出力をするには、以下の様なテンプレートを書かなければなりませんでした。

package main

import (
    "os"
    "text/template"
)

var tmpl = `
{{- range .}}
{{- if gt .Value 3 -}}
■{{.Name}}
{{- range .Years }}
  {{ . -}}年 受賞
{{- end}}
{{end -}}
{{- end -}}
`

type Foo struct {
    Name  string
    Value int
    Years []int
}

func main() {
    tpl := template.Must(template.New("").Parse(tmpl))
    tpl.Execute(os.Stdout, []Foo{
        {Name: "ぶどう", Value: 1, Years: []int{201120142019}},
        {Name: "みかん", Value: 2, Years: []int{2011}},
        {Name: "ぎょうざ", Value: 3, Years: []int{2020}},
        {Name: "ビーフストロガノフ", Value: 4, Years: []int{20112019}},
        {Name: "カレーライス", Value: 5, Years: []int{2012}},
        {Name: "満漢全席", Value: 6, Years: []int{20152019}},
    })
}
html/template, text/template: implement break and continue for range … · golang/go@d0dd26a · GitHub

…loops Break and continue for range loops was accepted as a proposal in June 2017. It was implemente...

https://github.com/golang/go/commit/d0dd26a88c019d54f22463daae81e785f5867565

continue を使える様になった事で、ループのネストが下げられる様になりました。

package main

import (
    "os"
    "text/template"
)

var tmpl = `
{{- range .}}
{{- if le .Value 3 -}}{{continue}}{{end -}}
■{{.Name}}
{{- range .Years }}
  {{ . -}}年 受賞
{{- end}}
{{end -}}
`

type Foo struct {
    Name  string
    Value int
    Years []int
}

func main() {
    tpl := template.Must(template.New("").Parse(tmpl))
    tpl.Execute(os.Stdout, []Foo{
        {Name: "ぶどう", Value: 1, Years: []int{201120142019}},
        {Name: "みかん", Value: 2, Years: []int{2011}},
        {Name: "ぎょうざ", Value: 3, Years: []int{2020}},
        {Name: "ビーフストロガノフ", Value: 4, Years: []int{20112019}},
        {Name: "カレーライス", Value: 5, Years: []int{2012}},
        {Name: "満漢全席", Value: 6, Years: []int{20152019}},
    })
}

サンプルが単純な物なので効果が伝わりにくいですが、テンプレートを書く手間が少し楽になった気がします。また break が入った事で if にマッチしない部分が無駄にループを回す事もなくなったので、Go 側から巨大な配列を事前にカットして渡す必要も無くなる事になります。

Posted at by