דילוג לתוכן המרכזי
מודול 10 · שיעור 2 מתוך 547/50 בקורס~14 min
שיעורי מודול (2/5)

טיפול בשגיאות אידיומטי

טיפול בשגיאות ב-Go הוא מפורש: אין חריגים, אין ניסיון/תפוס. כל פונקציה שיכולה להיכשל מחזירה את error בתור הערך האחרון שלה, והמתקשר מחליט. פרק זה אוסף את הדפוסים האידיומטים כדי לייצר, להפיץ ולבדוק שגיאות בצורה חזקה.

כלל הזהב

Go
data, err := os.ReadFile(path)
if err != nil {
    return fmt.Errorf("config %s: %w", path, err)
}

ארבעה עקרונות:

  1. error היא ההחזר האחרון.
  2. בדוק מיד עם if err != nil.
  3. הוסף הקשר לפני ההפצה (עם fmt.Errorf + %w).
  4. לעולם אל תתעלם משגיאה: או לטפל בה, לרשום אותה או להחזיר אותה.
Go
_ = file.Close() // EXPLICITLY ignored (you know what you're doing)

שגיאות זקיף

שגיאת זקיף היא משתנה גלובלי מיוצא var ErrXxx = errors.New("...") שהמתקשר יכול להשוות מולו:

Go
package store

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

func Get(key string) (string, error) {
    if !exists(key) {
        return "", ErrNotFound
    }
    // ...
}

המתקשר משתמש ב-errors.Is (לא ==), אשר הולך בשרשרת העוטפת:

Go
v, err := store.Get(k)
if errors.Is(err, store.ErrNotFound) {
    // handle 404
}

דוגמאות סטנדרטיות: io.EOF, sql.ErrNoRows, os.ErrNotExist.

שגיאות הקלדה מותאמת אישית

כדי לשאת נתונים מובנים (קוד HTTP, שדה לא חוקי וכו'), הגדר סוג:

Go
type ValidationError struct {
    Field string
    Msg   string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}

המתקשר משתמש ב-errors.As כדי להוריד בבטחה לאורך השרשרת:

Go
var ve *ValidationError
if errors.As(err, &ve) {
    fmt.Println("invalid field:", ve.Field)
}

errors.As הולך בשרשרת Unwrap() עד שהוא מוצא שגיאה שניתן להקצות למצביע שהועבר פנימה.

גלישה עם %w

fmt.Errorf עם הפועל %w מייצר שגיאה שעוטפת את המקורית, ומשמרת אותה עבור errors.Is/errors.As:

Go
if err := db.Query(); err != nil {
    return fmt.Errorf("load users: %w", err)
}

ללא %w (באמצעות %v או %s) אתה מקבל מחרוזת עשירה יותר אבל מאבד את השרשרת: המתקשר כבר לא יכול לזהות שגיאות ספציפיות.

כללים עבור %w:

  • רק אחת לכל שיחה fmt.Errorf (ריבוי גלישה דורש errors.Join).
  • הארגומנט חייב להיות error, לא מחרוזת.
  • הוסף הקשר, אל תשכפל את ההודעה המקורית.
Go
// bad: duplicates
return fmt.Errorf("error: %w", err) // "error: file not found"

// good: useful context
return fmt.Errorf("load config %s: %w", path, err)

errors.Join עבור שגיאות מרובות

מאז Go 1.20, errors.Join(errs...) משלב שגיאות מרובות לאחת ששומרת את כולן ניתנות לזיהוי על ידי errors.Is/As:

Go
var errs []error
for _, item := range items {
    if err := process(item); err != nil {
        errs = append(errs, fmt.Errorf("item %s: %w", item.ID, err))
    }
}
if len(errs) > 0 {
    return errors.Join(errs...)
}

מתי להשתמש ב-panic

לעולם לא לטעויות צפויות. רק בשלושה מקרים:

  1. באג שלא ניתן לשחזר: אי משתנה שהופר במקום ש"לא יכול לקרות" (panic("unreachable")).
  2. אתחול נכשל ב-init()/main() של תוכנית עצמאית (חסרה תצורה קריטית → כשל מהיר).
  3. ממשק API של ספרייה שמתעד זאת במפורש: למשל, regexp.MustCompile (נכנס לפאניקה אם התבנית לא חוקית - מקובל כי התבנית היא קבועה של זמן בנייה).

כל השאר error. אל תשתמש ב-panic בתור קיצור דרך להפצה: זה שובר חיבור, מדלג על defer Close() ומציף את המתקשר.

תרגילים

פעילות גופנית#go.m10.l2.e1
ניסיונות: 0טוען...

Wrappa l'errore di os.ReadFile con fmt.Errorf usando il verbo %w e aggiungendo il contesto 'config:' (con il path).

טוען עורך...

הפתרון זמין לאחר 3 ניסיונות

פעילות גופנית#go.m10.l2.e2
ניסיונות: 0טוען...

שגיאות ארה"ב.Is per verificare se err nella catena di wrap corrisponde alla sentinella ErrNotFound e stampa 'manca!' בטל קאסו.

טוען עורך...

הפתרון זמין לאחר 3 ניסיונות

Quiz#go.m10.l2.e3
מוכן

האם יש פאניקה ב-una libreria Go?

Go
func F(x int) {
  // ??? panic(...)
}
אפשרויות תשובה