Go Academy · درس

تغليف الأخطاء وإلغاء تغليفها

استخدام %w وerrors.Is وerrors.As

الدرس 3 من 412 خطوة

تغليف الأخطاء وإلغاء تغليفها درس مجاني في Go Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Go Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Go Academy 4 دروس في المجموع.

ما تغليف الأخطاء؟

يُضمّن تغليف الأخطاء خطأً أصليًا داخل خطأ جديد، مع الحفاظ على الخطأ الأصلي لفحصه برمجيًا وإضافة سياق. وقد أُدخل بشكل صحيح في Go 1.13:

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

var ErrDB = errors.New("database unreachable")

func queryUsers() error {
    return fmt.Errorf("queryUsers: %w", ErrDB)
}

func loadProfile(id int) error {
    return fmt.Errorf("loadProfile(%d): %w", id, queryUsers())
}

func main() {
    err := loadProfile(42)
    fmt.Println(err) // loadProfile(42): queryUsers: database unreachable
}

عامل %w

استخدم %w (وليس %v) في fmt.Errorf لتغليف خطأ. ويمكن الوصول إلى الخطأ المغلّف عبر errors.Unwrap:

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

func main() {
    inner := errors.New("inner error")
    outer := fmt.Errorf("outer: %w", inner)

    fmt.Println(outer)              // outer: inner error
    fmt.Println(errors.Unwrap(outer)) // inner error
    fmt.Println(errors.Is(outer, inner)) // true
}

errors.Unwrap

تُرجع errors.Unwrap الخطأ التالي في السلسلة (الخطأ المغلّف)، أو nil إذا لم يوجد:

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

type Layer struct{ msg string; err error }
func (l *Layer) Error() string { return l.msg }
func (l *Layer) Unwrap() error { return l.err }

func main() {
    root := errors.New("root")
    mid  := &Layer{"mid", root}
    top  := &Layer{"top", mid}

    fmt.Println(errors.Unwrap(top))  // mid
    fmt.Println(errors.Unwrap(mid))  // root
    fmt.Println(errors.Unwrap(root)) // <nil>
}

errors.Is — اجتياز السلسلة

تفك errors.Is(err, target) تغليف سلسلة الأخطاء بشكل متكرر حتى تعثر على تطابق أو تصل إلى nil:

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

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

func findItem(id int) error {
    return fmt.Errorf("findItem(%d): %w",
        id, fmt.Errorf("repo: %w", ErrNotFound))
}

func main() {
    err := findItem(7)
    fmt.Println(errors.Is(err, ErrNotFound)) // true — deep in chain
    fmt.Println(err)                         // full message chain
}

errors.As — اجتياز السلسلة للبحث عن الأنواع

تجتاز errors.As(err, &target) السلسلة بحثًا عن خطأ يمكن إسناده إلى نوع target:

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

type StatusError struct{ Code int }
func (e *StatusError) Error() string { return fmt.Sprintf("status %d", e.Code) }

func doRequest() error {
    return fmt.Errorf("request: %w", &StatusError{503})
}

func main() {
    err := doRequest()
    var se *StatusError
    if errors.As(err, &se) {
        fmt.Println("status code:", se.Code) // 503
    }
}

Unwrap مخصّص للسلاسل

طبّق Unwrap() error على نوع الخطأ المخصّص لدعم errors.Is وerrors.As:

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

type AppError struct {
    Op  string
    Err error
}
func (e *AppError) Error() string  { return fmt.Sprintf("%s: %v", e.Op, e.Err) }
func (e *AppError) Unwrap() error  { return e.Err }

var ErrAuth = errors.New("unauthorized")

func main() {
    err := &AppError{"login", ErrAuth}
    fmt.Println(errors.Is(err, ErrAuth)) // true
}

%v مقابل %w في fmt.Errorf

الفرق بين %v و%w:

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

func main() {
    inner := errors.New("inner")

    withV := fmt.Errorf("outer: %v", inner) // string only, no wrap
    withW := fmt.Errorf("outer: %w", inner) // wraps inner

    fmt.Println(errors.Is(withV, inner)) // false — not wrapped
    fmt.Println(errors.Is(withW, inner)) // true  — wrapped
}

التغليف عمليًا: معالج HTTP

يجعل تغليف الأخطاء بشكل متسق تصحيح الأخطاء أسهل في أنظمة الإنتاج:

package main
import "fmt"

func dbQuery(sql string) error {
    return fmt.Errorf("db.Query(%q): connection refused", sql)
}

func getUserByID(id int) (string, error) {
    if err := dbQuery("SELECT * FROM users WHERE id=?"); err != nil {
        return "", fmt.Errorf("getUserByID(%d): %w", id, err)
    }
    return "Alice", nil
}

func handler() error {
    _, err := getUserByID(1)
    if err != nil {
        return fmt.Errorf("handler: %w", err)
    }
    return nil
}

func main() { fmt.Println(handler()) }

errors.Join — تغليفات متعددة (Go 1.20 فأحدث)

تنشئ errors.Join خطأً يغلّف أخطاء متعددة. وتفحص errors.Is جميع هذه الأخطاء:

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

var ErrA = errors.New("err A")
var ErrB = errors.New("err B")

func main() {
    joined := errors.Join(ErrA, ErrB)
    fmt.Println(joined)
    fmt.Println(errors.Is(joined, ErrA)) // true
    fmt.Println(errors.Is(joined, ErrB)) // true
}

أفضل ممارسات فك التغليف

إرشادات لتغليف الأخطاء:

  • غلّف باستخدام %w للحفاظ على الخطأ من أجل فحصه برمجيًا
  • أضف اسم العملية للسياق: fmt.Errorf("funcName: %w", err)
  • لا تغلّف أخطاء Sentinel إذا لم يكن المتصلون سيستخدمون errors.Is
  • تجنب تغليف السياق نفسه مرتين في كل مستوى
  • طبّق Unwrap() على الأنواع المخصّصة للمشاركة في السلاسل

تحقق سريع

ما الفرق بين استخدام %v و%w في fmt.Errorf؟

مراجعة: تغليف الأخطاء وفك تغليفها

الملخص:

  • يغلّف fmt.Errorf("ctx: %w", err) الخطأ مع سياق
  • تحصل errors.Unwrap على الخطأ التالي في السلسلة
  • تجتاز errors.Is السلسلة بحثًا عن تطابق الهوية
  • تجتاز errors.As السلسلة بحثًا عن تطابق النوع
  • تطبّق الأنواع المخصّصة Unwrap() error
  • تغلّف errors.Join أخطاء متعددة (Go 1.20 فأحدث)
البدء مجانًا

تعلم Go مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
51
الدروس
203

الأسئلة الشائعة

هل درس «تغليف الأخطاء وإلغاء تغليفها» مجاني؟

نعم — نص درس «تغليف الأخطاء وإلغاء تغليفها» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Go Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Go Academy 4 دروس في المجموع.

ماذا ستتعلم في «تغليف الأخطاء وإلغاء تغليفها»؟

استخدام %w وerrors.Is وerrors.As تتمرن على Go Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Go Academy؟

لا تُشترط خبرة سابقة. Go Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «تغليف الأخطاء وإلغاء تغليفها»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Go Academy هذا؟

نعم. كل درس في Go Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. واجهة error
  2. إنشاء أخطاء مخصصة
  3. تغليف الأخطاء وإلغاء تغليفها
  4. ‏panic وrecover وdefer
← العودة إلى Go Academy