Reference · เก็บไว้เปิดตอนเขียนโค้ด

Go Day 4 — Cheat Sheet

ครอบคลุมสไลด์ทั้ง 66 หน้า · ตัวอย่างทุกอันรันจริงบน Go 1.26 แล้ว · พิมพ์ลงกระดาษได้

สารบัญ

  1. Underlying Type & Alias
  2. Interface & Duck Typing
  3. any, Assertion, Type Switch
  4. Standard Interfaces
  5. Error Architecture
  6. JSON & Struct Tags
  7. Encoder / Decoder
  8. ตารางกับดักรวม

1 · Underlying Type & Alias

type Score float64       // defined type — type ใหม่ ไม่เท่ากับ float64
type Price = float64     // alias      — คือ float64 ตัวเดียวกันเป๊ะ
type T Utype T = U
type ใหม่✅❌
assign ตรง ๆ กับ U❌ ต้อง U(v)✅
แปะ method✅❌ cannot define new methods on non-local type
%Tmain.TU
type Score float64
func (s Score) Passed() bool { return s >= 50 }   // method บน type ที่ไม่ใช่ struct ก็ได้

a := Score(80.5) + Score(10)   // 90.5  · %T = main.Score (ไม่ใช่ float64)

2 · Interface & Duck Typing

type Notifier interface {
    Notify(message string) error
}

type EmailNotifier struct{}
func (EmailNotifier) Notify(message string) error { return nil }
// ไม่ต้องเขียน implements — มี method ครบก็ใช้ได้เลย (structural typing)

Method set — กฎที่ทำให้ compile ไม่ผ่านบ่อยที่สุด

receiverT เป็น interface ได้*T เป็น interface ได้
func (t T) M()✅✅
func (t *T) M()❌✅
// error ที่จะเจอ:
// T does not implement I (method M has pointer receiver)
// ทางแก้: ใส่ & → var i I = &T{}

Dependency injection ด้วย interface

type PaymentGateway interface { Pay(amount int64) error }

type CheckoutService struct { gateway PaymentGateway }

func NewCheckoutService(gateway PaymentGateway) *CheckoutService {
    return &CheckoutService{gateway: gateway}
}
// สลับ PaypalGateway{} / GooglePayGateway{} / FakeGateway{} ได้ที่จุดเดียว

Interface embedding

type Storage interface {
    Saver      // รวม method ของ Saver มา ไม่ใช่ field
    Loader
    Deleter
}
// ของจริง: io.ReadWriter = io.Reader + io.Writer

กฎออกแบบ

3 · any, Type Assertion, Type Switch

type any = interface{}   // alias · interface ที่ไม่ขอ method อะไรเลย
ตัวแปร interface เก็บ สองช่องเสมอ:

  ┌──────────┬──────────┐
  │  type    │  value   │      %T อ่านช่องซ้าย · %v อ่านช่องขวา
  └──────────┴──────────┘
  interface == nil ก็ต่อเมื่อ ทั้งสองช่องว่าง
เขียนถ้า type ไม่ตรงใช้เมื่อ
s := i.(string)💥 panicมั่นใจ 100% เท่านั้น
s, ok := i.(string)s = zero value, ok = falseค่าเริ่มต้น ใช้อันนี้
switch v := i.(type)ตกลง defaultเช็คหลาย type
switch value := v.(type) {     // .(type) เขียนได้เฉพาะใน switch
case string:  // value เป็น string จริง ๆ ในบล็อกนี้ ไม่ต้อง assert ซ้ำ
case int:
case nil:     // เช็ค nil interface ได้
case fmt.Stringer, error:   // หลาย type ใน case เดียว → value กลับเป็น any
default:
}

4 · Standard Interfaces ที่ควรรู้จัก

InterfaceMethodได้อะไร
fmt.StringerString() stringfmt.Println พิมพ์ตามที่คุณกำหนด
errorError() stringเป็น error ได้
io.ReaderRead([]byte) (int, error)เสียบเข้ากับ ecosystem ทั้งหมดของ Go
io.WriterWrite([]byte) (int, error)เขียนลงไฟล์/network/buffer ได้เหมือนกันหมด
json.MarshalerMarshalJSON() ([]byte, error)คุมรูปแบบ JSON เอง
func (p Person) String() string { return p.Name + " " + p.Surname }

fmt.Println(p)   // มี String()  → Alice Wonder
                   // ไม่มี        → {Alice Wonder}

5 · Error Architecture

type error interface { Error() string }   // built-in · แค่นี้จริง ๆ

สร้าง error

ใช้เมื่อห่อได้== ได้
errors.New("...")ข้อความคงที่ / sentinel❌✅
fmt.Errorf("...%w", err)เติมบริบท / ห่อ error เดิม✅❌
&MyError{...}ต้องพก field ไปด้วย✅ (ถ้ามี Unwrap)❌

Sentinel + Error chain

var ErrUserNotFound = errors.New("user not found")   // ระดับ package · ขึ้นต้น Err

// repository: fmt.Errorf("query user: %w", err)
// service:    fmt.Errorf("get profile %s: %w", id, err)
// ได้: get profile U-101: query user: user not found

%v ทำให้โซ่ขาด แต่ข้อความเหมือนเดิมทุกตัวอักษร — errors.Is จะคืน false เงียบ ๆ ใช้ %w เท่านั้นเวลาห่อ error

ตรวจ error

errors.Is(err, target)errors.As(err, &target)
ถาม"มี error ตัวนี้ ในโซ่ไหม""มี error ชนิดนี้ ในโซ่ไหม"
คู่กับsentinelcustom struct
ได้กลับboolbool + ตัวจริงพร้อม field
ต้องมี &❌✅ บังคับ
if errors.Is(err, ErrUserNotFound) { /* ตอบ 404 */ }

var appErr *AppError
if errors.As(err, &appErr) {
    http.Error(w, appErr.Message, appErr.HTTPStatus)
}

Custom error struct — โครงที่ใช้จริง

type AppError struct {
    Op         string   // เกิดที่ไหน — "UserService.GetUser"
    Code       string   // รหัสให้เครื่องอ่าน — "USER_NOT_FOUND"
    HTTPStatus int      // ตอบ client ยังไง
    Message    string   // ข้อความให้ผู้ใช้อ่าน
    Err        error    // error เดิม
}

func (e *AppError) Error() string { ... }
func (e *AppError) Unwrap() error { return e.Err }   // ⚠️ ขาดไม่ได้

typed nil — คืน var e *AppError = nil จากฟังก์ชันที่ประกาศคืน error จะทำให้ err != nil เป็น true เพราะช่อง type ไม่ว่าง · ให้เขียน return nil ตรง ๆ

6 · JSON & Struct Tags

json.Marshal(v)            // Go → []byte  (ส่งออก)
json.Unmarshal(data, &v)   // []byte → Go  (รับเข้า) · ลืม & = error
json.MarshalIndent(v, "", "  ")   // สวยงามสำหรับ log/debug

Tag options

Tagผล
json:"userId"เปลี่ยนชื่อ key
json:"nickname,omitempty"ตัดทิ้งถ้าเป็น zero value
json:"-"ไม่เข้าไม่ออก JSON เลย
json:"-,"key ชื่อ "-" จริง ๆ (สังเกต comma)
json:",string"ส่งตัวเลขเป็น string ("2590")
ไม่ใส่ tagใช้ชื่อ field ตรง ๆ → "UserID"
// หลาย package ในบรรทัดเดียว คั่นด้วยเว้นวรรค:
UserID int `json:"userId" xml:"userId" validate:"required"`

กฎที่ทำให้ข้อมูลหายแบบไม่มี error

สถานการณ์ผลมี error ไหม
field ขึ้นต้นตัวเล็กข้ามทั้งขาเข้าและขาออก❌ เงียบสนิท
omitempty + ค่าเป็น 0/falseหายไปจาก JSON❌
JSON ไม่ส่ง field มาได้ zero value❌
JSON มี field ส่วนเกินทิ้งไปเฉย ๆ❌
key ตัวพิมพ์ไม่ตรง ("ID" vs "id")เข้าได้ (case-insensitive)❌
type ไม่ตรงไม่เข้า✅ มี error
ลืม & ตอน Unmarshalไม่เข้า✅ Unmarshal(non-pointer …)

Pointer แยก "ไม่ส่ง" ออกจาก "ส่งค่า zero"

type Item struct {
    Price  *int  `json:"price,omitempty"`
    Active *bool `json:"active,omitempty"`
}

Item{}                                // {}
Item{Price: &price, Active: &active}  // {"price":0,"active":false}

ปกป้องข้อมูลลับ — แยก struct ปลอดภัยกว่า tag

type UserModel struct { ID int; Email string; PasswordHash string }   // ในระบบ
type UserResponse struct {                                       // ส่งออก
    ID    int    `json:"id"`
    Email string `json:"email"`
}

Google JSON Style Guide (ที่คอร์สอ้างถึง)

7 · Encoder / Decoder — เมื่อ Marshal ไม่พอ

Marshal/UnmarshalEncoder/Decoder
ทำงานกับ[]byte ในมือio.Reader/io.Writer (stream)
หน่วยความจำเท่าขนาดข้อมูลทั้งก้อนคงที่
เหมาะกับข้อมูลเล็ก ที่มีอยู่แล้วไฟล์ใหญ่, HTTP body, network
โหมดเข้มงวด❌✅ DisallowUnknownFields()
file, err := os.Open("product.json")
if err != nil { return err }
defer file.Close()

decoder := json.NewDecoder(file)
decoder.DisallowUnknownFields()          // → json: unknown field "isAdmin"

var product Product
if err := decoder.Decode(&product); err != nil { return err }
// ฝั่งเขียนออก — ไม่ต้องสร้าง []byte กลาง:
json.NewEncoder(w).Encode(response)   // w คือ http.ResponseWriter ก็ได้

JSON ที่ไม่รู้โครงสร้าง

var m map[string]any
json.Unmarshal(data, &m)

m["id"]     // 101 แต่ %T = float64  ← ตัวเลขทุกตัวเป็น float64!
m["tags"]   // %T = []interface{}

8 · ตารางกับดักรวม — อ่านก่อนส่ง PR

โค้ดที่เห็นสิ่งที่คุณคิดสิ่งที่เกิดจริง
var v float64 = score (Score) ข้างใต้เป็น float64 ก็น่าจะได้ 💥 compile error · ต้อง float64(score)
type T = U แล้วแปะ method ได้ method บน type ใหม่ 💥 cannot define new methods on non-local type
var i I = T{} เมื่อ method ใช้ *T Go แปลงให้เอง 💥 method has pointer receiver · ใส่ &
s := i.(string) ไม่ตรงก็ได้ค่าว่าง 💥 panic · ใช้ s, ok := …
fmt.Sprintf("%v", p) ใน String() พิมพ์ค่า struct 💥 วนไม่จบ → stack overflow
fmt.Errorf("...: %v", err) ห่อ error แล้ว โซ่ขาด · errors.Is = false เงียบ ๆ
custom error ไม่มี Unwrap() field Err พอแล้ว errors.Is มองไม่ทะลุ
return e เมื่อ e คือ *AppError ที่เป็น nil ผู้เรียกได้ nil 💥 err != nil เป็น true (typed nil)
err.Error() == "user not found" เช็ค error ได้ พังทันทีที่มีคนแก้ข้อความ · ใช้ errors.Is
field password string + tag tag ทำให้ออก JSON ได้ หายเงียบทั้งสองทาง (unexported)
Price int `json:",omitempty"` ค่า 0 ส่ง 0 ออกไป หายไปจาก JSON · ใช้ *int
JSON ส่ง "isAdmin" เกินมา ได้ error เตือน ทิ้งเงียบ · ต้อง DisallowUnknownFields()
json.Unmarshal(data, product) แก้ product ได้ Unmarshal(non-pointer …) · ใส่ &
m["id"] จาก map[string]any ได้ int ได้ float64 ทุกตัวเลข
func f() error ขึ้นบรรทัดใหม่ค่อย { จัดรูปแบบสวยงาม 💥 unexpected semicolon or newline before {