0Pricing
Go Academy · درس

إنشاء أخطاء مخصصة

‏errors.New وfmt.Errorf وأخطاء sentinel

إنشاء أخطاء مخصصة درس مجاني في Go Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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

يحقق أي نوع يحتوي على طريقة 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))
}

أخطاء Sentinel باستخدام errors.New

أخطاء Sentinel هي متغيرات على مستوى الحزمة لحالات الأخطاء المعروفة:

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:

  • متغيرات أخطاء Sentinel: ErrXxx (مثل ErrNotFound)
  • أنواع الأخطاء المخصّصة: XxxError (مثل ValidationError)
  • استخدم var على مستوى الحزمة لـ Sentinel، ونوع المؤشر للأخطاء الغنية بالبيانات
  • صدّرها ليستخدمها المتصلون مع errors.Is وerrors.As

متى تستخدم كل أسلوب

اختيار أسلوب التعامل مع الخطأ المناسب:

  • errors.New — بسيط، بلا بيانات إضافية، لفحص هوية Sentinel
  • fmt.Errorf — رسالة منسّقة مع تغليف اختياري
  • نوع مخصّص — عندما يحتاج المتصل إلى بيانات منظّمة (حقول أو رموز)
  • errors.Join — لتجميع أخطاء تحقق متعددة

تحقق سريع

ما الدالة التي تتيح استخراج نوع خطأ محدد من سلسلة أخطاء؟

مراجعة: الأخطاء المخصّصة

الملخص:

  • طبّق Error() string لإنشاء أنواع أخطاء مخصّصة
  • أضف Unwrap() error للمشاركة في سلاسل الأخطاء
  • استخدم errors.Is لفحص الهوية، وerrors.As لاستخراج النوع
  • أخطاء Sentinel: ErrXxx؛ الأنواع المخصّصة: XxxError
  • تجمع errors.Join أخطاء متعددة (Go 1.20 فأحدث)

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

هل درس «إنشاء أخطاء مخصصة» مجاني؟

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

ماذا ستتعلم في «إنشاء أخطاء مخصصة»؟

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

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

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

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

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

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

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

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

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