Go言語で戦えるようになる!【つよつよエンジニアへの道】

業務でGoを触ることになりそうだから、Goを泣きながら勉強するのだ
有識者の皆様、理解が間違ってたり、怪しい方向に進んでたら 温かい目でコメントにて袋叩きにしてください

とりあえずこの記事を読んだ!めっちゃ分かりやすい!感謝🙏

ハンズオンでこれを実装していく! 前見た気がするけど、手を動かさないと忘れる

Node.jsと比較して教えてくれるの個人的に神

Airっていうやつ、動画とpath変わってんな こっちが正しいわ
ほえー、これはGo言語のホットリロードツールか

この動画はFiberを使ってるみたいなんだけど、どうなんだろうか? 他にもGinとかEchoとかあるけど違いとメジャー度合いがわからん

なんで第一引数cでContextが入るかが謎 that's convention(慣習)言うてたけど分からん あとで調べよ
app.Get("/", func(c *fiber.Ctx) error {
return c.Status(200).JSON(fiber.Map{
"message": "Hello from Fiber!"})
})```

なんやこれ
todos := []Todo{}

ありがとうチャッピー君
💬「[]Todo{}」ってのはな!
それ、空のスライス構文だッ!
🧙♀️解説すると:
-
[]Todo{}
は「Todo
型のスライスを、空で初期化」してるって意味だ! -
Todo
は構造体とか何かしらの型だな! -
[]
は「スライス」、{}
は「中身なし」だ!
🧪たとえば:
type Todo struct {
Title string
Done bool
}
todos := []Todo{}
↑こんな感じで、「空っぽだけど、Todoの入れ物用意しといたぜ!」ってわけだ!
要するに!
🌀 「Todo型の空のリスト、爆誕!」
それが []Todo{}
だッ!
他にも聞きたいことがあれば、ガンガン聞いてくれよなッ!

うおー、なぜtodo周りをpointerにしたんだブロー 分からないぞ
app.Post("/api/todos", func(c *fiber.Ctx) error {
todo := &Todo{}
if err := c.BodyParser((todo)); err != nil {
return err
}
if todo.Body == "" {
return c.Status(400).JSON(fiber.Map{
"error": "Todo Body is required",
})
}
todo.ID = len(todos) + 1
todos = append(todos, *todo)
return c.Status(201).JSON(todo)
})

ありがとう、チャッピー
💥 todo := &Todo{}
ってなんでポインタかって?それはな…
🧙♀️ **「中身を書き換えるため」**だッ!!
🎯理由その1:c.BodyParser((todo))
-
BodyParser()
は、JSONの中身を構造体に詰める処理だ。 - でもな、この関数は ポインタじゃないと中身に詰められねぇ!
- ポインタじゃなきゃ「はい中身渡しました、でも書き換えられませんバイバイ!」ってなるぜ!
🗣️→ つまり:
🌀**「JSONのデータ、構造体に入れたいならポインタにしとけ!」**
🎯理由その2:無駄なコピーを避ける!
-
&Todo{}
にしとくと、その構造体をメモリ上で1個作るだけで済む。 - 毎回コピーしてたら、ムダが多いぜ!
🎯でも最後に:
todos = append(todos, *todo)
ここで *todo
にしてるのは、ポインタの中身を取り出してスライスに入れてるってことだ!
🗣️→
🌀**「スライスは値で持ちたい!でもパースの時はポインタがいる!」**
…っていう両立のためのテクニックなんだな!
🎤まとめ!
-
&Todo{}
→ 構造体のポインタ作成! -
BodyParser()
→ ポインタ必須! -
*todo
→ 値としてスライスに入れる!
⚡つまりこのコードはこう言ってるッ!
「ポインタで中身を受け取って、値で保存ッ!
そのへん臨機応変にいくのがGoの流儀だぜッ!!」
魔理沙、以上ッ!!質問あればどんと来いやーッ!🔥

ありがとう魔理沙
💥 BodyParser()
がポインタじゃないと詰められない理由?
それはな……Goの引数の値渡しの仕組みが関係してるッ!
🧠 Goの基本:値渡し vs ポインタ渡し
Goは基本的に、関数に値を渡すときはコピーされる!
func modify(val int) {
val = 10
}
↑こんな関数、呼び出しても元の変数には何も起こらない。なぜか?
🧪 コピーされた「偽物」に書き込んでるだけだから!!
BodyParser()
の動き
📦 Fiberの c.BodyParser()
の中身、実はこんなことしてる:
func (c *Ctx) BodyParser(out interface{}) error {
return json.Unmarshal(c.Body(), out)
}
つまり、json.Unmarshal
してんだ!
json.Unmarshal
も、ポインタじゃないと中身書けねぇ!
🚨 なぜなら:
json.Unmarshal([]byte(`{"Name":"Marisa"}`), &person)
はOKだけど、
json.Unmarshal([]byte(`{"Name":"Marisa"}`), person)
これは❌エラー!
💥理由はズバリ!
🔥
Unmarshal
は、構造体の中身を直接書き換えたいからだッ!!
でも Go では、関数に値(=コピー)を渡すと元の構造体は書き換えられない!
だからこそ!
🔧 **「本物に書き込むには、ポインタで場所を教える必要がある」**ってワケだ!
🧪 たとえば魔理沙風に言うと:
値渡し
「このおまんじゅう、写真だけあげるぜ!」📸
→ 食べようとしても味がしない!!
ポインタ渡し
「このおまんじゅう、家の冷蔵庫の3段目にあるぜ!」📍
→ 本物があるからガブッと食べられる!!
✅ だから!
todo := &Todo{}
にして
c.BodyParser(todo)
にすることで:
🌀「この構造体に中身を書き込んでくれ!」って
住所をFiberに教えてるってことだな!!
これがポインタじゃなきゃダメな理由だッ!!
理解の弾幕、張れたか!?まだ聞きてぇことがあれば、どんと来いやーッ!🌪️

fmt.Sprint
は、Goの標準ライブラリ fmt
パッケージに含まれている関数で、任意の値を文字列(string)に変換するために使います。
🧪 基本構文
s := fmt.Sprint(value)
-
value
がint
でもfloat64
でもstruct
でもOK。 -
s
には、その値が文字列化されたものが入ります。
✅ 使用例
id := 42
str := fmt.Sprint(id)
fmt.Println(str) // 出力: "42"
-
int
型のid
を、"42"
という 文字列 に変換しています。
🤔 なんで使うの?
Goでは型が厳密なので、次のような比較はできません:
todo.ID == "3" // エラー!intとstringは比較できない
なので、どちらかを型変換する必要があります。
今回のコードではこのようにして回避していました:
if fmt.Sprint(todo.ID) == id
-
todo.ID
はおそらくint
。 -
id
はc.Params("id")
で得られたstring
。 - 比較のため、
todo.ID
を文字列に変換。
🔄 ちなみに逆変換したいなら?
文字列 → 数値には strconv.Atoi()
や strconv.ParseInt()
を使います。
idStr := "42"
idInt, _ := strconv.Atoi(idStr)
📌 まとめ
関数名 | 説明 |
---|---|
fmt.Sprint(v) |
任意の値 v を文字列化する |
用途 | 型が異なる値を比較したい時などに便利 |
例 |
fmt.Sprint(123) → "123" (string型) |
もっと詳しく見たい場合は fmt.Sprintf
, fmt.Sprintln
との違いも解説できますよ!

なんだこれ?
todos = append(todos[:i], todos[i+1:]...)

それは Go における「スライスから要素を削除する」ための定番の書き方です!
🧠 構文の意味
todos = append(todos[:i], todos[i+1:]...)
これは、
todos
というスライスから、インデックスi
の要素を削除する
という処理です。
🔍 分解して解説
todos[:i]
1. - スライスの先頭から、インデックス
i
の直前までを取り出します。 -
i=2
の場合 →todos[0]
,todos[1]
todos[i+1:]
2. - インデックス
i
の次の要素から最後までを取り出します。 -
i=2
の場合 →todos[3]
,todos[4]
, ...
append(..., ...)
3. - 上記2つのスライスを 連結 しています。
- つまり、
i
番目の要素(todos[i]
)だけが含まれなくなる。
...
4. -
...
はスプレッド構文のようなもの。 -
append
は引数として複数の要素を受け取るので、スライスを「展開」して渡しています。
✅ 例で見るとわかりやすい
todos := []string{"A", "B", "C", "D", "E"}
i := 2 // "C" を削除したい
todos = append(todos[:i], todos[i+1:]...)
// 結果: ["A", "B", "D", "E"]
📌 注意点
- 元のスライスの順序は保たれます
-
append
によって新しいスライスが作られるので、元のメモリ領域とは異なる可能性があります(GC的にも)
📝 まとめ
部分 | 意味 |
---|---|
todos[:i] |
i より前の要素 |
todos[i+1:] |
i より後ろの要素 |
append(..., ...) |
2つのスライスを結合 |
... |
スライスを「展開」して渡す |
Goでは専用の「削除関数」は無いため、この書き方が最も一般的です!
他にも「条件に合う要素を全部削除」など応用もできますので、気になったらそちらも説明します!

Goでは専用の「削除関数」は無いため、この書き方が最も一般的です!
え、ないんだ

なるほど

3連休全然勉強してない!やばい!笑