0Pricing
Go Academy · บทเรียน

การสร้างข้อผิดพลาดแบบกำหนดเอง

errors.New, fmt.Errorf และข้อผิดพลาดแบบ sentinel

การสร้างข้อผิดพลาดแบบกำหนดเอง เป็นบทเรียน Go Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Go Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดจึงต้องใช้ชนิดข้อผิดพลาดแบบกำหนดเอง

ชนิดข้อผิดพลาดแบบกำหนดเองเก็บข้อมูลที่มีโครงสร้างนอกเหนือจากข้อความ ทำให้ผู้เรียกใช้ตรวจสอบและตอบสนองต่อเงื่อนไขข้อผิดพลาดที่เฉพาะเจาะจงได้:

package main
import "fmt"

// Custom error type carries extra context
type HTTPError struct {
    StatusCode int
    Message    string
}

func (e *HTTPError) Error() string {
    return fmt.Sprintf("HTTP %d: %s", e.StatusCode, e.Message)
}

func fetch(url string) error {
    return &HTTPError{404, "page not found"}
}

func main() {
    err := fetch("http://example.com")
    fmt.Println(err) // HTTP 404: page not found
}

การนำอินเทอร์เฟซของข้อผิดพลาดไปใช้

ชนิดข้อมูลใดก็ตามที่มีเมธอด Error() string จะทำตามอินเทอร์เฟซ error:

package main
import "fmt"

type ValidationError struct {
    Field   string
    Value   interface{}
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation: field %q value %v — %s", e.Field, e.Value, e.Message)
}

func validateAge(age int) error {
    if age < 0 || age > 150 {
        return &ValidationError{"age", age, "must be between 0 and 150"}
    }
    return nil
}

func main() {
    fmt.Println(validateAge(-5))
}

ข้อผิดพลาดค่าประจำด้วย errors.New

ข้อผิดพลาดค่าประจำคือตัวแปรระดับแพ็กเกจสำหรับเงื่อนไขข้อผิดพลาดที่รู้จักกันดี:

package main
import ("fmt"; "errors")

var (
    ErrNotFound      = errors.New("not found")
    ErrUnauthorized  = errors.New("unauthorized")
    ErrAlreadyExists = errors.New("already exists")
)

func getUser(id int) (string, error) {
    if id != 1 { return "", ErrNotFound }
    return "Alice", nil
}

func main() {
    _, err := getUser(99)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("user does not exist")
    }
}

fmt.Errorf ด้วย %w สำหรับการห่อข้อผิดพลาด

fmt.Errorf ที่ใช้ %w จะห่อข้อผิดพลาด โดยเก็บข้อผิดพลาดเดิมไว้ให้ errors.Is และ errors.As ใช้งาน:

package main
import ("fmt"; "errors")

var ErrDatabase = errors.New("database error")

func queryDB() error {
    return fmt.Errorf("queryDB: %w", ErrDatabase)
}

func main() {
    err := queryDB()
    fmt.Println(err)                       // queryDB: database error
    fmt.Println(errors.Is(err, ErrDatabase)) // true — unwrapped
}

ข้อผิดพลาดแบบกำหนดเองด้วย Unwrap

นำ Unwrap() error ไปใช้กับชนิดข้อผิดพลาดแบบกำหนดเอง เพื่อให้เข้าร่วมในสายโซ่ข้อผิดพลาดได้:

package main
import ("fmt"; "errors")

var ErrNotFound = errors.New("not found")

type QueryError struct {
    Query string
    Err   error
}

func (e *QueryError) Error() string {
    return fmt.Sprintf("query %q: %v", e.Query, e.Err)
}
func (e *QueryError) Unwrap() error { return e.Err }

func main() {
    err := &QueryError{"SELECT *", ErrNotFound}
    fmt.Println(err)
    fmt.Println(errors.Is(err, ErrNotFound)) // true
}

errors.Is — การตรวจสอบเอกลักษณ์

errors.Is จะเดินไปตามสายโซ่ข้อผิดพลาดเพื่อค้นหาข้อผิดพลาดที่เท่ากับเป้าหมาย:

package main
import ("fmt"; "errors")

var ErrTimeout = errors.New("timeout")

func deepCall() error {
    return fmt.Errorf("layer3: %w", fmt.Errorf("layer2: %w", ErrTimeout))
}

func main() {
    err := deepCall()
    fmt.Println(errors.Is(err, ErrTimeout)) // true — found deep in chain
}

errors.As — การดึงชนิดข้อมูล

errors.As ค้นหาข้อผิดพลาดรายการแรกในสายโซ่ที่ตรงกับชนิดข้อมูลเป้าหมาย แล้วกำหนดค่าให้กับเป้าหมายนั้น:

package main
import ("fmt"; "errors")

type PermissionError struct{ User string }
func (e *PermissionError) Error() string { return "permission denied for " + e.User }

func action(user string) error {
    return fmt.Errorf("action: %w", &PermissionError{user})
}

func main() {
    err := action("bob")
    var pe *PermissionError
    if errors.As(err, &pe) {
        fmt.Println("denied:", pe.User) // denied: bob
    }
}

การห่อข้อผิดพลาดหลายรายการ (Go 1.20 ขึ้นไป)

Go 1.20 เพิ่ม errors.Join และ fmt.Errorf ที่มี %w หลายรายการสำหรับรวมข้อผิดพลาด:

package main
import ("fmt"; "errors")

func validate(name, email string) error {
    var errs []error
    if name == ""  { errs = append(errs, errors.New("name required")) }
    if email == "" { errs = append(errs, errors.New("email required")) }
    return errors.Join(errs...)
}

func main() {
    err := validate("", "")
    fmt.Println(err)
    // name required
    // email required
}

หลักการตั้งชื่อชนิดข้อผิดพลาด

หลักการตั้งชื่อตัวแปรและชนิดข้อผิดพลาดใน Go:

  • ตัวแปรข้อผิดพลาดค่าประจำ: ErrXxx (เช่น ErrNotFound)
  • ชนิดข้อผิดพลาดแบบกำหนดเอง: XxxError (เช่น ValidationError)
  • ใช้ตัวแปรระดับแพ็กเกจสำหรับข้อผิดพลาดค่าประจำ และใช้ชนิดตัวชี้สำหรับข้อผิดพลาดที่มีข้อมูลมาก
  • ส่งออกเพื่อให้ผู้เรียกใช้ใช้กับ errors.Is / errors.As

เมื่อใดควรใช้แต่ละแนวทาง

การเลือกแนวทางจัดการข้อผิดพลาดที่เหมาะสม:

  • errors.New — เรียบง่าย ไม่มีข้อมูลเพิ่มเติม และใช้ตรวจสอบเอกลักษณ์ของข้อผิดพลาดค่าประจำ
  • fmt.Errorf — ข้อความที่จัดรูปแบบ พร้อมการห่อข้อผิดพลาดที่เลือกใช้ได้
  • ชนิดข้อมูลแบบกำหนดเอง — เมื่อผู้เรียกใช้ต้องการข้อมูลที่มีโครงสร้าง เช่น ฟิลด์หรือรหัส
  • errors.Join — รวมข้อผิดพลาดจากการตรวจสอบหลายรายการ

ตรวจสอบอย่างรวดเร็ว

ฟังก์ชันใดใช้ดึงชนิดข้อผิดพลาดที่เฉพาะเจาะจงออกจากสายโซ่ข้อผิดพลาดได้

สรุปทบทวน: ข้อผิดพลาดแบบกำหนดเอง

สรุป:

  • นำ Error() string ไปใช้เพื่อสร้างชนิดข้อผิดพลาดแบบกำหนดเอง
  • เพิ่ม Unwrap() error เพื่อให้เข้าร่วมในสายโซ่ข้อผิดพลาด
  • ใช้ errors.Is เพื่อตรวจสอบเอกลักษณ์ และ errors.As เพื่อดึงชนิดข้อมูล
  • ข้อผิดพลาดค่าประจำ: ErrXxx ส่วนชนิดข้อมูลแบบกำหนดเอง: XxxError
  • errors.Join ใช้รวมข้อผิดพลาดหลายรายการ (Go 1.20 ขึ้นไป)

คำถามที่พบบ่อย

บทเรียน “การสร้างข้อผิดพลาดแบบกำหนดเอง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การสร้างข้อผิดพลาดแบบกำหนดเอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Go Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การสร้างข้อผิดพลาดแบบกำหนดเอง”

errors.New, fmt.Errorf และข้อผิดพลาดแบบ sentinel คุณปฏิบัติ Go Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Go Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Go Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “การสร้างข้อผิดพลาดแบบกำหนดเอง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Go Academy นี้ได้ไหม

ได้ บทเรียน Go Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. interface error
  2. การสร้างข้อผิดพลาดแบบกำหนดเอง
  3. การห่อและคลายข้อผิดพลาด
  4. panic, recover และ defer
← กลับไปที่ Go Academy