Menu
Coddy logo textTech

errors.Is() ו-errors.As()

חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 56 מתוך 107.

כאשר עוטפים שגיאות, בדיקות שוויון פשוטות כמו err == ErrNotFound כבר לא עובדות. החבילה errors מספקת שתי פונקציות לבדיקת שרשראות של שגיאות עטופות: errors.Is() ו-errors.As().

errors.Is() בודקת אם שגיאה כלשהי בשרשרת תואמת לשגיאת sentinel מסוימת:

import "errors"

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

func FindUser(id int) error {
    return fmt.Errorf("database lookup failed: %w", ErrNotFound)
}

err := FindUser(42)
if errors.Is(err, ErrNotFound) {
    fmt.Println("User doesn't exist")
}

למרות שהשגיאה שהוחזרה עטופה, errors.Is() מסירה את העטיפות מהשרשרת ומוצאת בתוכה את ErrNotFound.

errors.As() בודקת אם שגיאה כלשהי בשרשרת תואמת לטיפוס מסוים ומחלצת אותה:

type ValidationError struct {
    Field string
}

func (e *ValidationError) Error() string {
    return "validation failed: " + e.Field
}

err := fmt.Errorf("request failed: %w", &ValidationError{Field: "email"})

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

השתמשו ב־errors.Is() כשבודקים שגיאות סמן, וב־errors.As() כשצריך לגשת לשדות של סוג שגיאה מותאם אישית. שתי הפונקציות עוברות על כל שרשרת השגיאות, ולכן הן חיוניות לטיפול נכון בשגיאות עטופות.

challenge icon

אתגר

קל

בואו נבנה מערכת לעיבוד קבצים שמדגימה איך לבדוק כראוי שרשראות של שגיאות עטופות באמצעות errors.Is() ו-errors.As(). תיצרו שגיאות שנעטפות כשהן עוברות דרך שכבות שונות, ואז תשתמשו בפונקציות האלה כדי לזהות אותן ולחלץ מהן מידע.

תארגנו את הקוד בשני קבצים:

  • errors.go: הגדירו את תשתית השגיאות עבור מערכת הקבצים שלכם.

    צרו שגיאת זקיף:

    • ErrPermissionDenied עם ההודעה "permission denied"

    צרו סוג שגיאה מותאם אישית:

    • מבנה FileError עם השדות Filename (מחרוזת) ו-Operation (מחרוזת)
    • המתודה Error() שלו צריכה להחזיר: [Operation] failed on file: [Filename]

    ממשו שתי פונקציות המדמות פעולות קבצים ועוטפות שגיאות:

    • ReadFile(filename string) error - אם שם הקובץ הוא "secret.txt", החזירו את ErrPermissionDenied. אם שם הקובץ הוא "missing.txt", החזירו *FileError עם "read" בתור Operation ועם Filename הנתון. אחרת, החזירו nil.
    • ProcessFile(filename string) error - קוראת ל-ReadFile. אם מוחזרת שגיאה, עטפו אותה באמצעות fmt.Errorf עם התבנית "processing failed: %w". אחרת, החזירו nil.
  • main.go: קראו שם קובץ מהקלט וקראו ל-ProcessFile. השתמשו ב-errors.Is() וב-errors.As() כדי לבדוק את שרשרת השגיאות העטופות ולהדפיס הודעות מתאימות בהתאם למה שתמצאו בתוכה.

הקלט הבא יסופק:

  • שורה 1: שם הקובץ לעיבוד

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

  • אם errors.Is() מוצאת את ErrPermissionDenied בשרשרת: הדפיסו את הודעת השגיאה המלאה, ואז בשורה חדשה הדפיסו Access denied - check file permissions
  • אם errors.As() מוצאת *FileError בשרשרת: הדפיסו את הודעת השגיאה המלאה, ואז בשורה חדשה הדפיסו File issue: [Filename] during [Operation]
  • אם לא מתרחשת שגיאה: הדפיסו File '[filename]' processed successfully

לדוגמה, עבור secret.txt, הפלט שלכם צריך להיות:

processing failed: permission denied
Access denied - check file permissions

ועבור missing.txt, הפלט שלכם צריך להיות:

processing failed: read failed on file: missing.txt
File issue: missing.txt during read

ועבור data.txt, הפלט שלכם צריך להיות:

File 'data.txt' processed successfully

שימו לב שאף על פי שהשגיאות נעטפות על ידי ProcessFile, עדיין אפשר לזהות את שגיאת הזקיף המקורית באמצעות errors.Is() ולחלץ את סוג השגיאה המותאם אישית באמצעות errors.As(). הפונקציות האלה עוברות על פני כל שרשרת השגיאות כדי למצוא את מה שאתם מחפשים.

נסו בעצמכם

package main

import (
	"errors"
	"fmt"
)

func main() {
	// קרא את שם הקובץ מהקלט
	var filename string
	fmt.Scanln(&filename)

	// קרא ל-ProcessFile עם שם הקובץ
	err := ProcessFile(filename)

	// TODO: טפל בתוצאה באמצעות בדיקת שרשרת השגיאות
	// 
	// השתמש ב-errors.Is() כדי לבדוק אם ErrPermissionDenied נמצא בשרשרת:
	// - אם נמצא: הדפס את השגיאה המלאה, ואז "Access denied - check file permissions"
	//
	// השתמש ב-errors.As() כדי לבדוק אם *FileError נמצא בשרשרת:
	// - אם נמצא: הדפס את השגיאה המלאה, ואז "File issue: [Filename] during [Operation]"
	//
	// אם אין שגיאה: הדפס "File '[filename]' processed successfully"

	_ = errors.Is // רמז: השתמש ב-errors.Is()
	_ = errors.As // רמז: השתמש ב-errors.As()
	_ = err
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Go אונליין