APIを書くならmapよりstructがしっくりきた

map は自由だがtypoに弱い

前回 は、sliceの lencap、切り出し、copy を整理しました。

最後に、APIを書くなら早めに分かっておきたい mapstruct を見ます。

map は自由な辞書です。

map の基本は、Go言語仕様の Map types(公式)にあります。

user := map[string]string{
    "name": "Bob",
    "pet":  "dog",
}

fmt.Println(user["name"]) // Bob

これはPythonの辞書っぽく見えます。自由で便利です。ただ、キー名を間違えてもコンパイルでは分かりません。

fmt.Println(user["nmae"]) // typoしてもコンパイルは通る

一方で struct は、決まったフィールドを持つデータ型です。

struct はAPIの形を先に置ける

存在しないフィールドは使えない

struct の基本は、Go言語仕様の Struct types(公式)にあります。

type User struct {
    Name string
    Age  int
    Pet  string
}

使うときはこうです。

u := User{
    Name: "Bob",
    Age:  50,
    Pet:  "dog",
}

fmt.Println(u.Name)

存在しないフィールドを使うと、コンパイルエラーになります。

// fmt.Println(u.Nmae) // NG

APIのrequest / responseを考えると、これはかなりありがたいです。

JSONタグで外側のキーに合わせる

type CreateUserRequest struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

type CreateUserResponse struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

Goの中では req.Namereq.Age として扱い、JSONでは nameage というキーになります。

JSONとの変換や json:"name" のようなタグは、encoding/json公式)を使うときに効いてきます。

{
  "name": "Bob",
  "age": 50
}

Pythonで辞書を使ってJSONを組み立てる感覚に近いところもあります。ただ、GoではAPIの形を struct として先に置けます。

map[string]any で柔軟に扱うより、request / response の形が決まっているなら struct にした方が、あとから読みやすそうです。

逆にPythonの強みも見えた

ローカル自動化はPythonが軽い

Goを少し触ってみて、逆にPythonの強みも改めて見えました。

Pythonは、ローカルでちょっとしたスクリプトを書いてすぐ実行するのが本当に楽です。python hello.py で動かせるし、ライブラリも豊富です。Bashやzshで書くと少しつらい処理でも、Pythonなら標準ライブラリや外部ライブラリを使って、いい感じに自動化できます。

たとえば、ファイルを読んで加工する、HTTPで何かを取ってくる、JSONを触る、CLIっぽい小さい道具を書く。こういうローカル作業だと、Pythonはかなり強いなと思いました。

APIや配布はGoで試したい

一方でGoは、go build してバイナリを作る流れがあります。これはAPIやアプリケーションとして配布したり、運用したりするには良さそうです。

ただ、手元でちょっと自動化したいだけなら、毎回コンパイルしてバイナリを作るのは少し重く感じる場面もありそうです。

このあたりは、たぶん向いている場所が違うんだと思います。

ローカルで雑に自動化するなら、BashかPythonの方が気軽です。少し複雑になって、ライブラリも使いたいならPythonがかなり強い。逆に、これから作ってみるアプリケーションやAPIは、Goで挑戦してみたいなと思いました。

全部をGoに置き換えたいというより、使いどころを分けられるようになりたい、という感じです。

Pythonと違う言語を触る意味

Goで見えた違い

今回少し触っただけでも、GoはPythonとだいぶ違うなと思いました。

Pythonは、書き始めるまでが軽いです。型やメモリのことをあまり考えなくても、まず処理を書けます。そこが好きです。

Goは、もう少し手前で形を決める感じがあります。Printf では出し方を指定する。struct ではデータの形を決める。sliceでは lencap を見られる。:= で推測してくれるところもあるけど、推測されたあとにはちゃんと型があります。

この「違う言語って、こういうことなんだな」という感覚が、今回けっこう面白かったです。

Goを選んだ理由は、Pythonの代わりに全部Goで書きたいからではありません。APIを書くときの運用や保守、依存関係、型、メモリの見え方を、別の言語からちゃんと見てみたかったからです。

そのうえで、これから作ってみるアプリケーションやAPIは、Goで試してみたいです。PythonはPythonで、ローカル自動化や小さいスクリプトを書くときにかなり強い。GoはGoで、APIやアプリケーションを形にしていくときに別の良さがありそう。この切り分けが少し見えたのが、今回けっこう大きかったです。

次は小さいAPIから始める

まだAPIは書き始めていません。次はたぶん、これくらいの小さいものから始めます。

GET /health
GET /users
POST /users

このシリーズでは、Goを体系的に全部解説するというより、自分が引っかかったところ、あとで見返したいところ、Pythonとの違いが見えたところを気軽にまとめていくつもりです。