עטיפת שגיאות (fmt.Errorf)
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 54 מתוך 107.
כאשר שגיאות עוברות דרך שכבות מרובות של היישום שלך, חשוב לדעת היכן מקורה של שגיאה. הפונקציה fmt.Errorf של Go, עם הפועל %w, מאפשרת לך לעטוף שגיאות תוך שימור השגיאה המקורית והוספת הקשר.
עטיפת שגיאות יוצרת שרשרת של שגיאות. כל שכבה יכולה להוסיף מידע על הפעולה שהיא ניסתה לבצע כשהשגיאה התרחשה:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// עבד את הנתונים...
return nil
}הפועל %w הוא מיוחד. הוא עוטף את השגיאה המקורית בתוך השגיאה החדשה. זה שונה מ־%v, שרק ממיר את השגיאה למחרוזת ומאבד את הזהות של השגיאה המקורית.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}כש־LoadSettings נכשלת, הודעת השגיאה מציגה את כל השרשרת: "loading settings: reading config file: open settings.json: no such file or directory". כל שכבה מוסיפה הקשר, וכך קל הרבה יותר לנפות שגיאות.
השגיאה העטופה עדיין מכילה בתוכה את השגיאה המקורית. בשיעור הבא תלמדו כיצד להסיר את העטיפה מהשגיאות האלה ולבדוק מה יש בתוכן באמצעות errors.Is() ו-errors.As().
אתגר
קלבואו נבנה מערכת לעיבוד הזמנות שמדגימה עטיפת שגיאות בכמה שכבות. תיצרו שרשרת של פונקציות שבה כל שכבה מוסיפה הקשר לשגיאות, וכך קל לאתר בדיוק היכן מתרחשות בעיות.
תארגנו את הקוד בשני קבצים:
orders.go: צרו את הלוגיקה לעיבוד הזמנות, עם כמה שכבות שעוטפות שגיאות כשהן עוברות כלפי מעלה.ממשו שלוש פונקציות שיוצרות שרשרת עיבוד:
ValidateItem(itemID string) error- מחזירה שגיאה עם ההודעה"item not found"אם הערך של itemID הוא"INVALID", אחרת מחזירהnilProcessOrder(orderID, itemID string) error- קוראת ל־ValidateItem. אם היא מחזירה שגיאה, עטפו אותה בהקשר:"processing order [orderID]: %w". אחרת, החזירוnilSubmitOrder(customerName, orderID, itemID string) error- קוראת ל־ProcessOrder. אם היא מחזירה שגיאה, עטפו אותה בהקשר:"submitting order for [customerName]: %w". אחרת, החזירוnil
כל שכבה צריכה להשתמש ב־
fmt.Errorfעם הפועל%wכדי לעטוף את השגיאה מהשכבה שמתחתיה וכך לבנות שרשרת של הקשרים.main.go: קראו את פרטי ההזמנה מהקלט, קראו ל־SubmitOrderוהציגו את התוצאה. אם מתרחשת שגיאה, הדפיסו את שרשרת השגיאות המלאה. אם הפעולה מצליחה, הדפיסו הודעת אישור.
הקלטים הבאים יסופקו:
- שורה 1: שם הלקוח
- שורה 2: מזהה ההזמנה
- שורה 3: מזהה הפריט
הדפיסו את התוצאה:
- אם מתרחשת שגיאה:
Error: [full error chain] - אם הפעולה מצליחה:
Order [orderID] submitted successfully for [customerName]
לדוגמה, בהינתן Alice, ORD-123 ו־INVALID, הפלט שלכם צריך להיות:
Error: submitting order for Alice: processing order ORD-123: item not foundשימו לב איך הודעת השגיאה מציגה את השרשרת המלאה. אפשר לעקוב אחר הבעיה מניסיון שליחת ההזמנה ברמה העליונה, דרך עיבוד ההזמנה, ועד לכשל האימות עצמו. כל שכבה הוסיפה הקשר משלה באמצעות %w.
ובהינתן Bob, ORD-456 ו־ITEM-001, הפלט שלכם צריך להיות:
Order ORD-456 submitted successfully for Bobנסו בעצמכם
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// קרא את שם הלקוח
scanner.Scan()
customerName := scanner.Text()
// קרא את מזהה ההזמנה
scanner.Scan()
orderID := scanner.Text()
// קרא את מזהה הפריט
scanner.Scan()
itemID := scanner.Text()
// TODO: קרא ל-SubmitOrder עם ערכי הקלט
// TODO: אם מתרחשת שגיאה, הדפס: Error: [שרשרת השגיאות המלאה]
// TODO: אם הפעולה הצליחה, הדפס: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP ב-Go
קבצים חיצונייםסביבת העבודה והמודולים של Goחבילות וייבואשמות מיוצאים לעומת שמות לא מיוצאיםמבוא ל-OOP ב-GoStructs כמחלקותהגדרת מתודות עבור Structsמקבלי Pointer לעומת Valueאתחול Structפונקציות Constructorחזרה – מחשבון פשוט4ממשקים
מבוא לממשקיםמימוש משתמעממשק כחוזהממשק ריק (any)אימות טיפוסמתג טיפוסיםהרכבת ממשקיםממשקי Stringer ו-Errorסיכום – מחשבון צורות7כימוס
שדות מיוצאים לעומת שדות לא מיוצאיםכימוס ברמת החבילהמתודות Getter ו-Setterהסתרת מידע ב-Goסיכום – רשומות תלמידים10Generics (Go 1.18 ומעלה)
מבוא ל־Genericsפרמטרים של טיפוסיםאילוצי טיפוסיםמבנים גנרייםפתרון עוקף למתודות גנריותסיכום – אוסף גנרי2העמקה בסוגים וב-Structs
סוגים בסיסיים ומורכביםהגדרות של סוגים מותאמים אישיתתגיות StructStructs אנונימייםStructs מקונניםערכי אפס וברירות מחדלסיכום – ספר אנשי קשר5קומפוזיציה במקום ירושה
למה ב-Go אין ירושהיסודות הטמעת Structקידום מתודותהטמעת כמה Structsהטמעה לעומת אגרגציההסתרת מתודות מוטמעותסיכום – היררכיית עובדים8טיפול בשגיאות ו-OOP
ממשק errorסוגי שגיאות מותאמים אישיתעטיפת שגיאות (fmt.Errorf)שגיאות Sentinelerrors.Is() ו-errors.As()Panic, Defer ו-Recoverסיכום – מנתח קבצים11ספרייה סטנדרטית ותכנות מונחה עצמים
io.Reader ו-io.Writersort.Interfaceהממשק fmt.Stringerencoding/json עם מבניםהממשק http.Handlerסיכום – מודלים של REST API3מצביעים וזיכרון
יסודות המצביעים ב-Goמצביעים למבניםהעברה לפי ערך לעומת לפי הפניההפונקציה new()איסוף אשפה ב-Goסיכום - בונה רשימה מקושרת6פולימורפיזם ב-Go
פולימורפיזם באמצעות ממשקיםטיפוס ברווז ב-Goכללי התאמה לממשקאוספים פולימורפייםהזרקת תלויותסיכום - מעבד תשלומים9מקביליות ו-OOP
יסודות GoroutinesChannels ותקשורתChannel עם חוצץ לעומת ללא חוצץפקודת Selectsync.Mutex ו-sync.RWMutexsync.WaitGroupתכנון Struct בטוח לשימוש במקבילסיכום - מאגר עובדים12מושגים מתקדמים בתכנות מונחה עצמים
דפוס האפשרויות הפונקציונליותדפוס Builder ב-Goשרשור מתודותכינויי טיפוסים לעומת הגדרותיסודות Reflectionסקירה כללית של יצירת קודתרגלו בעצמכם: קומפיילר Go אונליין