การจัดการ Error
Error เป็น value ไม่ใช่ exception
หัวข้อที่มีชื่อว่า “Error เป็น value ไม่ใช่ exception”TypeScript (และ JavaScript) ใช้ throw/try/catch จัดการ error — exception จะไหลขึ้นไปตาม call stack แบบมองไม่เห็น ส่วน Go เลือกทางที่ต่างออกไป: error เป็นเพียง value ฟังก์ชันที่อาจล้มเหลวจะคืน error เป็นค่าสุดท้าย แล้วผู้เรียกตัดสินใจได้ทันทีว่าจะจัดการยังไงต่อ
ตอนแรกอาจรู้สึกว่าเยิ่นเย้อ แต่พอใช้ไปสักพักคุณจะชอบที่ทุกเส้นทางที่อาจล้มเหลวนั้นชัดเจนและตามรอยได้
รูปแบบพื้นฐาน
หัวข้อที่มีชื่อว่า “รูปแบบพื้นฐาน”// TypeScript: exceptionsasync function readConfig(path: string): Promise<Config> { try { const data = await fs.readFile(path, "utf-8"); return JSON.parse(data); } catch (err) { throw new Error(`failed to read config: ${err}`); }}// Go: errors as return valuesfunc readConfig(path string) (Config, error) { data, err := os.ReadFile(path) if err != nil { return Config{}, fmt.Errorf("failed to read config: %w", err) } var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return Config{}, fmt.Errorf("failed to parse config: %w", err) } return cfg, nil}การสร้าง error
หัวข้อที่มีชื่อว่า “การสร้าง error”// TypeScriptthrow new Error("something went wrong");
class NotFoundError extends Error { constructor(public id: string) { super(`item ${id} not found`); }}// Goimport "errors"import "fmt"
err := errors.New("something went wrong")
// Sentinel error (comparable with errors.Is)var ErrNotFound = errors.New("not found")
// Custom error typetype NotFoundError struct{ ID string }func (e *NotFoundError) Error() string { return fmt.Sprintf("item %s not found", e.ID)}การ wrap และ unwrap ด้วย %w
หัวข้อที่มีชื่อว่า “การ wrap และ unwrap ด้วย %w”package main
import ( "errors" "fmt")
var ErrNotFound = errors.New("not found")
type ValidationError struct { Field string Message string}
func (e *ValidationError) Error() string { return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)}
func findUser(id int) error { if id <= 0 { return &ValidationError{Field: "id", Message: "must be positive"} } if id > 100 { return fmt.Errorf("findUser: %w", ErrNotFound) } return nil}
func main() { // errors.Is unwraps the chain err := findUser(999) if errors.Is(err, ErrNotFound) { fmt.Println("user not found (sentinel matched)") }
// errors.As extracts the concrete type err = findUser(-1) var ve *ValidationError if errors.As(err, &ve) { fmt.Printf("bad field: %s — %s\n", ve.Field, ve.Message) }}Loading Go runtime (first run only, ~8 MB)…