x.(T): استخراج القيمة من واجهة
تخفي قيمة الواجهة النوع المحدّد الذي تحمله. ويطلب تأكيد النوع x.(T) استعادته.
المخرجات:
gopher 6
0 false
string of length 6
يجب أن يكون لـ x نوع واجهة. التأكيد على نوع محدّد يفشل في الترجمة: invalid operation: s (variable of type string) is not an interface.
الصيغة ذات القيمة الواحدة تسبّب panic
عندما يكون التأكيد خاطئًا، تسبّب الصيغة ذات القيمة الواحدة panic برسالة تسمّي النوعين:
المخرجات:
recovered: interface conversion: interface {} is int, not string
استخدم الصيغة ذات القيمة الواحدة فقط عندما يكون النوع المختلف خطأ برمجيًا تريد أن ينهار البرنامج بسببه. وفي كل مكان آخر استخدم comma-ok. عند الفشل تعطي comma-ok القيمة الصفرية لـ T وfalse.
التأكيد على واجهة nil يفشل أيضًا: تسبّب var v any; v.(string) حالة panic برسالة interface conversion: interface {} is nil, not string، وتعيد صيغة comma-ok القيمتين "", false.
التأكيد إلى واجهة أخرى
يمكن أن يكون T نوع واجهة. ينجح التأكيد عندها إذا كانت القيمة الديناميكية تطبّق T، وتحتفظ النتيجة بالقيمة الديناميكية نفسها. هكذا تتحقّق المكتبة القياسية من القدرات الاختيارية.
تتحقّق io.Copy من أن مصدرها يطبّق io.WriterTo وتستخدمه إن فعل. وتتحقّق fmt من Stringer وerror. يتيح هذا النمط للواجهة الصغيرة أن تبقى صغيرة بينما يستفيد المستدعون من التطبيقات الأغنى.
type switch
عندما قد تكون القيمة أحد عدة أنواع، يحلّ type switch محل سلسلة من التأكيدات. والصيغة x.(type) صالحة فقط داخل switch.
المخرجات:
hi
integer 7
integer 8
3.14
nil
error: boom
unhandled []int
قواعد تستحق المعرفة:
- في حالة بنوع واحد يكون لـ
xذلك النوع. وفي حالة تسرد عدة أنواع، وفيdefault، يكون لـxنوع الواجهة الأصلية (anyهنا). - تُفحص الحالات بالترتيب. ضع الواجهات الأكثر تحديدًا قبل الأعم، لأن القيمة قد تحقّق عدة واجهات.
- لا تطابق
case nilإلا واجهة nil، لا واجهة تحمل مؤشرًا nil. - لا يوجد
fallthroughفي type switch.
errors.As: التأكيد للأخطاء المغلّفة
تُغلَّف الأخطاء غالبًا مع سياق: fmt.Errorf("load config: %w", err). التأكيد المباشر لا ينظر إلا إلى الخطأ الخارجي ويفوّت الذي بداخله. أما errors.As فتمشي على السلسلة.
المخرجات:
type assertion finds it: false
errors.As finds it: open /no/such/file
استخدم تأكيد النوع على الأخطاء فقط عندما تعرف أن الخطأ غير مغلّف، وهذا عمليًا يعني تقريبًا أبدًا. تشرح صفحة الأخطاء المخصّصة errors.As وerrors.Is وتعريف أنواع أخطائك.
التأكيدات مقابل التحويلات مقابل الأنواع العامة
| لديك | تريد | استخدم |
|---|---|---|
int | float64 | تحويل: float64(n) |
any يحمل int | الـ int | تأكيد: v.(int) |
any من عدة أنواع ممكنة | التفرّع حسب النوع | type switch |
error قد يكون مغلّفًا | نوع خطأ محدّد | errors.As |
| دالة تعمل مع أنواع كثيرة | أمان وقت الترجمة | الأنواع العامة |
الشيفرة المليئة بـ any وtype switch كثيرًا ما تكون علامة على أن الأنواع العامة أو واجهة مناسبة ستعبّر عن القصد أفضل وتلتقط الأخطاء وقت الترجمة.
أخطاء شائعة
- استخدام الصيغة المسبّبة لـ panic على بيانات غير موثوقة. JSON المفكوك، وقيم الخرائط من النوع
any، ومدخلات الإضافات: استخدم comma-ok دائمًا. - التأكيد إلى النوع الرقمي الخطأ. تُفكّ أرقام JSON إلى
anyكـfloat64، فتفشلv.(int)معها. - تأكيد نوع قيمة عندما يكون المخزّن مؤشرًا. إذا كانت الواجهة تحمل
*User، تفشلv.(User). أكّدv.(*User). - تأكيدات النوع على الأخطاء. استخدم
errors.As.
الأسئلة الشائعة
ما هو تأكيد النوع في Go؟
تعبير x.(T) حيث x قيمة واجهة. إذا كان T نوعًا محدّدًا، يعيد القيمة المخزّنة في x بالنوع T. وإذا كان T نوع واجهة، يتحقّق من أن القيمة المخزّنة تطبّق T أيضًا. الصيغة ذات القيمة الواحدة تسبّب panic عند فشل الفحص؛ والصيغة ذات القيمتين v, ok := x.(T) تبلغ عنه في ok.
كيف أعرف نوع قيمة واجهة في Go؟
استخدم type switch: switch v := x.(type) { case int: ...; case string: ...; default: ... }. داخل كل حالة يكون لـ v نوع تلك الحالة. ولنوع واحد يكون تأكيد comma-ok s, ok := x.(string) أقصر. وتطبع fmt.Printf("%T", x) اسم النوع لتتبّع الأخطاء.
ما الفرق بين تأكيد النوع وتحويل النوع في Go؟
يحوّل التحويل T(x) قيمة من نوع إلى آخر، مثل float64(n)؛ ويقرّر المترجم هل هو مسموح، ولا يمكن أن يفشل وقت التشغيل. أما التأكيد x.(T) فيعمل فقط على قيم الواجهات ويتحقّق وقت التشغيل من النوع المخزّن بداخلها. لا يمكنك كتابة int(x) لقيمة any تحمل int؛ تحتاج x.(int).
هل أستخدم تأكيد النوع لفحص أنواع الأخطاء؟
لا. استخدم errors.As(err, &target). التأكيد المباشر err.(*MyError) لا ينظر إلا إلى الخطأ الخارجي، فيفشل حالما يُغلَّف الخطأ بـ fmt.Errorf("...: %w", err). أما errors.As فتمشي على سلسلة التغليف.