ستقدّم هذه المقالة جميع أصناف الأنواع في Go والمفاهيم المتعلقة بنظام الأنواع في Go. وبدون معرفة هذه المفاهيم الأساسية، يصعب الحصول على فهم شامل لـ Go.
مفهوم: الأنواع الأساسية
قُدّمت الأنواع الأساسية المدمجة في Go أيضاً في الأنواع الأساسية المدمجة والقيم الحرفية الأساسية. ومن أجل اكتمال هذه المقالة، أُدرجت هذه الأنواع الأساسية المدمجة هنا مرة أخرى.
- نوع النصوص المدمج:
string. - النوع المنطقي المدمج:
bool. - الأنواع العددية المدمجة: -
int8،uint8(byte)،int16،uint16،int32(rune)،uint32،int64،uint64،int،uint،uintptr. float32،float64.complex64،complex128.
لاحظ أن byte اسم مستعار مدمج لـ uint8، وأن rune اسم مستعار مدمج لـ int32. ويمكننا أيضاً تعريف أسماء مستعارة مخصّصة للأنواع (انظر أدناه).
باستثناء أنواع النصوص، لن تحاول سلسلة مقالات Go 101 شرح المزيد عن الأنواع الأساسية الأخرى.
الأنواع الأساسية المدمجة السبعة عشر هي أنواع مُعلنة مسبقاً.
مفهوم: الأنواع المركّبة
يدعم Go الأنواع المركّبة التالية:
- أنواع المؤشرات - تشبه مؤشرات C.
- أنواع البُنى - تشبه بُنى C.
- أنواع الدوال - الدوال أنواع من الدرجة الأولى في Go.
- أنواع الحاويات: - أنواع المصفوفات - أنواع حاويات ذات طول ثابت.
- نوع الشرائح - أنواع حاويات ذات طول وسعة ديناميكيين.
- أنواع الخرائط - الخرائط مصفوفات ترابطية (أو قواميس). يطبّق مصرِّف Go القياسي الخرائط كجداول تجزئة.
- أنواع القنوات - تُستخدم القنوات لمزامنة البيانات بين goroutines (الخيوط الخضراء في Go).
- أنواع الواجهات - تؤدي الواجهات دوراً رئيسياً في الانعكاس وتعدد الأشكال.
يمكن التعبير عن الأنواع المركّبة غير المسمّاة بما يقابلها من القيم الحرفية للأنواع. فيما يلي بعض الأمثلة على التمثيل الحرفي لجميع أصناف الأنواع المركّبة غير المسمّاة (ستُشرح الأنواع المسمّاة وغير المسمّاة أدناه).
// افترض أن T نوع عشوائي وأن Tkey نوع
// يدعم المقارنة (== و !=).
*T // نوع مؤشر
[5]T // نوع مصفوفة
[]T // نوع شريحة
map[Tkey]T // نوع خريطة
// نوع بنية
struct {
name string
age int
}
// نوع دالة
func(int) (bool, string)
// نوع واجهة
interface {
Method0(string) int
Method1() (int, bool)
}
// بعض أنواع القنوات
chan T
chan<- T
<-chan T
ستُشرح الأنواع القابلة للمقارنة وغير القابلة للمقارنة أدناه.
حقيقة: أصناف الأنواع
كل نوع من الأنواع الأساسية والمركّبة المذكورة أعلاه يقابل صنفاً واحداً من أصناف الأنواع. وإلى جانب هذه الأصناف، تنتمي أنواع المؤشرات غير الآمنة المقدَّمة في الحزمة القياسية unsafe أيضاً إلى صنف واحد من أصناف الأنواع في Go. لذا، حتى الآن (Go 1.25)، يمتلك Go 26 صنفاً من الأنواع.
الصياغة: تعريفات الأنواع
(كان تعريف النوع، أو إعلان تعريف النوع، المسمّى في البداية تعريف النوع، الطريقة الوحيدة لتعريف الأنواع قبل Go 1.9. ومنذ Go 1.9، أصبح تعريف النوع أحد شكلين من تعريفات الأنواع. والشكل الجديد يُسمّى تعريف الاسم المستعار للنوع، وسيُقدَّم في قسم أدناه.)
في Go، يمكننا تعريف أنواع جديدة باستخدام الصيغة التالية. وفي هذه الصياغة، type كلمة مفتاحية.
// تعريف نوع جديد منفرد.
type NewTypeName SourceType
// تعريف عدة أنواع جديدة معاً.
type (
NewTypeName1 SourceType1
NewTypeName2 SourceType2
)
يجب أن تكون أسماء الأنواع الجديدة معرّفات. لكن لاحظ أن أسماء الأنواع المعرَّفة على مستوى الحزمة لا يمكن أن تكون init.
يتضمن تعريف النوع الثاني في المثال أعلاه مواصفتين للنوع. وإذا احتوى تعريف النوع على أكثر من مواصفة نوع واحدة، فيجب إحاطة مواصفات النوع بزوج من ().
لاحظ أن:
- النوع المعرَّف الجديد ونوعه المصدر المقابل في تعريفات الأنواع هما نوعان مختلفان.
- أي نوعين معرَّفين في تعريفي نوعين مختلفين هما دائماً نوعان مختلفان.
- النوع المعرَّف الجديد والنوع المصدر يتشاركان النوع الكامن نفسه (انظر أدناه لتعريف الأنواع الكامنة)، ويمكن تحويل قيمهما إحداهما إلى الأخرى.
- يمكن تعريف الأنواع داخل أجسام الدوال.
بعض أمثلة تعريف الأنواع:
// الأنواع الجديدة المعرَّفة والأنواع المصدر التالية كلها
// أنواع أساسية. والأنواع المصدر كلها مُعلنة مسبقاً.
type (
MyInt int
Age int
Text string
)
// الأنواع الجديدة المعرَّفة والأنواع المصدر التالية كلها
// أنواع مركّبة. والأنواع المصدر كلها غير مسمّاة.
type IntPtr *int
type Book struct{author, title string; pages int}
type Convert func(in0 int, in1 bool)(out0 int, out1 string)
type StringArray [5]string
type StringSlice []string
func f() {
// أسماء الأنواع الثلاثة المعرَّفة
// يمكن استخدامها داخل الدالة فقط.
type PersonAge map[string]int
type MessageQueue chan string
type Reader interface{Read([]byte) int}
}
لاحظ أنه من Go 1.9 إلى Go 1.17، اعتبرت مواصفة Go أن الأنواع المدمجة المُعلنة مسبقاً هي أنواع معرَّفة. لكن منذ Go 1.18، توضّح مواصفة Go أن الأنواع المدمجة المُعلنة مسبقاً ليست أنواعاً معرَّفة.
مفهوم: الأنواع العامة المخصّصة والأنواع المجسَّدة
منذ Go 1.18، يدعم Go الأنواع العامة المخصّصة (والدوال). ويجب تجسيد النوع العام حتى يُستخدم كأنواع للقيم.
النوع العام نوع معرَّف، وأنواعه المجسَّدة أنواع مسمّاة (ستُشرح الأنواع المسمّاة في القسم التالي).
والمفهومان المهمان الآخران في الأجناس العامة المخصّصة هما القيود ووسائط الأنواع.
لا يتناول هذا الكتاب الأجناس العامة المخصّصة بالتفصيل. يرجى قراءة كتاب Go Generics 101 لمعرفة كيفية تعريف واستخدام الأنواع والدوال العامة.
مفهوم: الأنواع المسمّاة مقابل الأنواع غير المسمّاة
قبل Go 1.9، كان مصطلح النوع المسمّى معرَّفاً بدقة في مواصفة Go. كان النوع المسمّى يُعرَّف بأنه نوع يُمثَّل بمعرّف. ومع ميزة الاسم المستعار المخصّص للأنواع المقدَّمة في Go 1.9 (انظر القسم التالي)، أُزيل مصطلح النوع المسمّى من مواصفة Go واستُبدل بمصطلح النوع المعرَّف. ومنذ Go 1.18، ومع إدخال الأجناس العامة المخصّصة، أُعيد مصطلح النوع المسمّى إلى مواصفة Go.
قد يكون النوع المسمّى
- نوعاً مُعلناً مسبقاً (لا يشمل الاسم المستعار للنوع)؛
- نوعاً معرَّفاً (غير عام مخصّص)؛
- نوعاً مجسَّداً (لنوع عام)؛
- نوع وسيط نوع (يُستخدم في الأجناس العامة المخصّصة).
وتسمّى أنواع القيم الأخرى أنواعاً غير مسمّاة. ويجب أن يكون النوع غير المسمّى نوعاً مركّباً (وليس العكس).
الصياغة: تعريفات الأسماء المستعارة للأنواع
منذ Go 1.9، يمكننا تعريف أسماء مستعارة مخصّصة للأنواع باستخدام الصياغة التالية. تشبه صياغة تعريف الاسم المستعار تعريف النوع كثيراً، لكن لاحظ وجود = في كل مواصفة اسم مستعار للنوع.
type (
Name = string
Age = int
)
type table = map[string]int
type Table = map[Name]Age
يجب أن تكون أسماء الأسماء المستعارة للأنواع معرّفات. ومثل تعريفات الأنواع، يمكن أيضاً تعريف الأسماء المستعارة للأنواع داخل أجسام الدوال.
اسم النوع (أو قيمته الحرفية) وأسماؤه المستعارة كلها تدل على النوع نفسه. بموجب التعريفات أعلاه، Name اسم مستعار لـ string، لذا كلاهما يدل على النوع نفسه. وينطبق الشيء نفسه على الأزواج الثلاثة الأخرى من ترميزات الأنواع (سواء أسماء أم قيماً حرفية):
Ageوinttableوmap[string]intTableوmap[Name]Age
في الواقع، القيمتان الحرفيتان map[string]int و map[Name]Age تدلان أيضاً على النوع نفسه. لذا، وبالمثل، يدل الاسمان المستعاران table و Table أيضاً على النوع نفسه.
لاحظ أنه على الرغم من أن الاسم المستعار للنوع يمتلك دائماً اسماً، فقد يدل على نوع غير مسمّى. على سبيل المثال، يدل الاسمان المستعاران table و Table على النوع غير المسمّى نفسه map[string]int.
مفهوم: الأنواع الكامنة
في Go، لكل نوع نوع كامن (underlying type). والقواعد هي:
- بالنسبة للأنواع المدمجة، فإن الأنواع الكامنة المقابلة هي الأنواع نفسها.
- بالنسبة لنوع
Pointerالمعرَّف في حزمة الشيفرة القياسيةunsafe، فإن نوعه الكامن هو نفسه (على الأقل يمكننا التفكير بذلك. في الواقع، النوع الكامن لنوعunsafe.Pointerغير موثّق جيداً. ويمكننا أيضاً التفكير بأن النوع الكامن هو*T، حيث يمثلTنوعاً عشوائياً). ويُنظر إلىunsafe.Pointerأيضاً كنوع مدمج. - النوع الكامن لنوع غير مسمّى، والذي يجب أن يكون نوعاً مركّباً، هو نفسه.
- في تعريف النوع، يكون للنوع المعرَّف حديثاً والنوع المصدر النوع الكامن نفسه.
أمثلة:
// الأنواع الكامنة للأنواع التالية هي int في كلتا الحالتين.
type (
MyInt int
Age MyInt
)
// الأنواع الجديدة التالية لها أنواع كامنة مختلفة.
type (
IntSlice []int // النوع الكامن هو []int
MyIntSlice []MyInt // النوع الكامن هو []MyInt
AgeSlice []Age // النوع الكامن هو []Age
)
// الأنواع الكامنة لـ []Age و Ages و AgeSlice
// كلها النوع غير المسمّى []Age.
type Ages AgeSlice
كيف يمكن تتبّع النوع الكامن بالنظر إلى نوع معرَّف من قبل المستخدم؟ القاعدة هي أنه عند الوصول إلى نوع أساسي مدمج أو نوع غير مسمّى، يجب إيقاف التتبّع. خذ تعريفات الأنواع المعروضة أعلاه كأمثلة، ولنتتبّع أنواعها الكامنة.
MyInt → int Age → MyInt → int IntSlice → []int MyIntSlice → []MyInt → []int AgeSlice → []Age → []MyInt → []int Ages → AgeSlice → []Age → []MyInt → []int
في Go،
- تسمّى الأنواع التي أنواعها الكامنة
boolأنواعاً منطقية؛ - تسمّى الأنواع التي أنواعها الكامنة أي نوع من أنواع الأعداد الصحيحة المدمجة أنواعاً صحيحة؛
- تسمّى الأنواع التي أنواعها الكامنة إما
float32أوfloat64أنواعاً عشرية؛ - تسمّى الأنواع التي أنواعها الكامنة إما
complex64أوcomplex128أنواعاً مركّبة؛ - تسمّى الأنواع الصحيحة والعشرية والمركّبة أيضاً أنواعاً عددية؛
- تسمّى الأنواع التي أنواعها الكامنة
stringأنواع نصوص.
يؤدي مفهوم النوع الكامن دوراً مهماً في تحويلات القيم والإسنادات والمقارنات في Go.
مفهوم: القيم
تسمّى نسخة من نوع ما «قيمة» من ذلك النوع. وتتشارك قيم النوع نفسه بعض الخصائص المشتركة. وقد يمتلك النوع قيماً كثيرة، وإحداها هي القيمة الصفرية للنوع.
لكل نوع قيمة صفرية، يمكن النظر إليها كالقيمة الافتراضية للنوع. ويمكن استخدام المعرّف nil المُعلن مسبقاً لتمثيل القيم الصفرية للشرائح والخرائط والدوال والقنوات والمؤشرات (بما فيها المؤشرات غير الآمنة) والواجهات. لمزيد من المعلومات عن nil، يرجى قراءة nil في Go.
هناك عدة أصناف من أشكال تمثيل القيم في الشيفرة، تشمل القيم الحرفية، والثوابت المسمّاة، والمتغيرات، والتعبيرات، مع أن الثلاثة الأولى يمكن النظر إليها كحالات خاصة من الأخيرة.
ويمكن أن تكون القيمة مُنمَّطة أو غير مُنمَّطة.
قُدّمت جميع أصناف القيم الحرفية الأساسية في مقالة الأنواع الأساسية والقيم الحرفية. وهناك صنفان آخران من القيم الحرفية في Go: القيم الحرفية المركّبة والقيم الحرفية للدوال.
القيم الحرفية للدوال، كما يوحي الاسم، تُستخدم لتمثيل قيم الدوال. ويتكوّن تعريف الدالة من قيمة حرفية للدالة ومعرّف (اسم الدالة).
تُستخدم القيم الحرفية المركّبة لتمثيل قيم أنواع البُنى وأنواع الحاويات (المصفوفات والشرائح والخرائط). يرجى قراءة البُنى في Go والحاويات في Go لمزيد من التفاصيل.
لا توجد قيم حرفية لتمثيل قيم المؤشرات والقنوات والواجهات.
مفهوم: أجزاء القيم
في وقت التشغيل، تُخزَّن قيم كثيرة في مكان ما في الذاكرة. وفي Go، لكل قيمة من هذه القيم جزء مباشر. لكن بعضها يمتلك جزءاً غير مباشر واحداً أو أكثر. ويشغل كل جزء من القيمة مقطعاً متصلاً من الذاكرة. وتُشار إلى الأجزاء الكامنة غير المباشرة للقيمة من قِبل جزءها المباشر عبر مؤشرات (آمنة أو غير آمنة).
مصطلح جزء القيمة غير معرَّف في مواصفة Go. وهو مستخدم فقط في Go 101 لجعل بعض الشروح أبسط ولمساعدة مبرمجي Go على فهم أنواع وقيم Go بشكل أفضل.
مفهوم: أحجام القيم
عند تخزين قيمة في الذاكرة، يُسمّى عدد البايتات التي يشغلها الجزء المباشر من القيمة حجم القيمة. وبما أن جميع قيم النوع نفسه لها حجم القيمة نفسه، فغالباً ما نسمّي هذا ببساطة حجم النوع.
يمكننا استخدام دالة Sizeof في الحزمة القياسية unsafe للحصول على حجم أي قيمة.
لا تحدّد مواصفة Go متطلبات حجم القيمة للأنواع غير العددية. ومتطلبات أحجام القيم لجميع أصناف الأنواع العددية الأساسية مدرجة في مقالة الأنواع الأساسية والقيم الحرفية الأساسية.
مفهوم: النوع القاعدي لنوع المؤشر
بالنسبة لنوع مؤشر، افترض أن نوعه الكامن يمكن التعبير عنه حرفياً بـ *T، فعندئذٍ يُسمّى T النوع القاعدي لنوع المؤشر.
يمكن العثور على مزيد من المعلومات عن أنواع المؤشرات وقيمها في مقالة المؤشرات في Go.
مفهوم: حقول نوع البنية
يتكوّن نوع البنية من مجموعة تعريفات لمتغيرات أعضاء. ويُسمّى كل تعريف من تعريفات متغيرات الأعضاء «حقل» من حقول نوع البنية. على سبيل المثال، نوع البنية Book التالي له ثلاثة حقول: author و title و pages.
type Book struct {
author string
title string
pages int
}
يمكن العثور على مزيد من المعلومات عن أنواع البُنى وقيمها في مقالة البُنى في Go.
مفهوم: توقيع أنواع الدوال
يتكوّن توقيع نوع الدالة من قائمة تعريف وسائط الإدخال وقائمة تعريف نتائج الإخراج للدالة.
اسم الدالة وجسمها ليسا جزءين من توقيع الدالة. وأنواع الوسائط والنتائج مهمة لتوقيع الدالة، لكن أسماء الوسائط والنتائج ليست مهمة.
يرجى قراءة الدوال في Go لمزيد من التفاصيل عن أنواع الدوال وقيم الدوال.
مفهوم: الطرق ومجموعة الطرق لنوع ما
في Go، يمكن أن تمتلك بعض الأنواع طرقاً. ويمكن أيضاً تسمية الطرق دوال الأعضاء. وتتكوّن مجموعة طرق النوع من جميع طرق النوع.
مفهوم: النوع الديناميكي والقيمة الديناميكية لقيمة واجهة
قيم الواجهات هي قيم أنواعها أنواع واجهات.
يمكن لكل قيمة واجهة أن تحتوي داخلها على قيمة غير واجهة. وتُسمّى القيمة المحتواة داخل قيمة واجهة القيمة الديناميكية لقيمة الواجهة. ويُسمّى نوع القيمة الديناميكية النوع الديناميكي لقيمة الواجهة. وقيمة الواجهة التي لا تحتوي على شيء هي قيمة واجهة صفرية. والقيمة الصفرية للواجهة ليس لها قيمة ديناميكية ولا نوع ديناميكي.
يمكن لنوع الواجهة أن يحدّد صفراً أو عدة طرق، تشكّل مجموعة طرق نوع الواجهة.
إذا كانت مجموعة طرق نوع ما، سواء أكان نوع واجهة أم نوعاً غير واجهة، مجموعة شاملة (super set) لمجموعة طرق نوع واجهة، نقول إن النوع يطبّق نوع الواجهة.
لمزيد من المعلومات عن أنواع الواجهات وقيمها، يرجى قراءة الواجهات في Go.
مفهوم: القيمة الملموسة والنوع الملموس لقيمة ما
بالنسبة لقيمة غير واجهة (مُنمَّطة)، فإن قيمتها الملموسة هي نفسها ونوعها الملموس هو نوع القيمة.
القيمة الصفرية للواجهة ليس لها نوع ملموس ولا قيمة ملموسة. وبالنسبة لقيمة واجهة غير صفرية، فإن قيمتها الملموسة هي قيمتها الديناميكية ونوعها الملموس هو نوعها الديناميكي.
مفهوم: أنواع الحاويات
يمكن النظر إلى المصفوفات والشرائح والخرائط كأنواع حاويات رسمية.
وأحياناً يمكن أيضاً النظر إلى أنواع النصوص والقنوات كأنواع حاويات بشكل غير رسمي.
ولكل قيمة من نوع حاوية رسمي أو غير رسمي طول.
يمكن العثور على مزيد من المعلومات عن أنواع الحاويات الرسمية وقيمها في مقالة الحاويات في Go.
مفهوم: نوع المفتاح لنوع الخريطة
إذا أمكن التعبير عن النوع الكامن لنوع خريطة حرفياً بـ map[Tkey]T، فعندئذٍ يُسمّى Tkey نوع المفتاح لنوع الخريطة. ويجب أن يكون Tkey نوعاً قابلاً للمقارنة (انظر أدناه).
مفهوم: نوع العنصر لنوع الحاوية
يجب أن تكون أنواع العناصر المخزّنة في قيم نوع حاوية متطابقة. ويُسمّى النوع المتطابق للعناصر نوع العنصر لنوع الحاوية.
- بالنسبة لنوع مصفوفة، إذا كان نوعه الكامن
[N]T، فإن نوع عنصره هوT. - بالنسبة لنوع شريحة، إذا كان نوعه الكامن
[]T، فإن نوع عنصره هوT. - بالنسبة لنوع خريطة، إذا كان نوعه الكامن
map[Tkey]T، فإن نوع عنصره هوT. - بالنسبة لنوع قناة، إذا كان نوعه الكامن
chan Tأوchan<- Tأو<-chan T، فإن نوع عنصره هوT. - نوع العنصر لأي نوع نصوص هو دائماً
byte(المعروف أيضاً بـuint8).
مفهوم: اتجاهات أنواع القنوات
يمكن النظر إلى قيم القنوات كطوابير متزامنة تتبع مبدأ الوارد أولاً يخرج أولاً (FIFO). ولأنواع القنوات وقيمها اتجاهات.
- قيمة قناة قابلة للإرسال والاستقبال معاً تسمّى قناة ثنائية الاتجاه. ويُسمّى نوعها نوع قناة ثنائية الاتجاه. ويُعبَّر عن الأنواع الكامنة لأنواع القنوات ثنائية الاتجاه بالقيمة الحرفية
chan T. - قيمة قناة قابلة للإرسال فقط تسمّى قناة للإرسال فقط. ويُسمّى نوعها نوع قناة للإرسال فقط. ويُعبَّر عن أنواع القنوات للإرسال فقط بالقيمة الحرفية
chan<- T. - قيمة قناة قابلة للاستقبال فقط تسمّى قناة للاستقبال فقط. ويُسمّى نوعها نوع قناة للاستقبال فقط. ويُعبَّر عن أنواع القنوات للاستقبال فقط بالقيمة الحرفية
<-chan T.
يمكن العثور على مزيد من المعلومات عن أنواع القنوات وقيمها في مقالة القنوات في Go.
حقيقة: الأنواع التي تدعم المقارنات أو لا تدعمها
حالياً (Go 1.25)، لا يدعم Go المقارنات (بالمعاملين == و !=) لقيم الأنواع التالية:
- أنواع الشرائح
- أنواع الخرائط
- أنواع الدوال*. أي نوع بنية يحتوي على حقل نوعه غير قابل للمقارنة وأي نوع مصفوفة نوع عنصره غير قابل للمقارنة.
تسمّى الأنواع المذكورة أعلاه أنواعاً غير قابلة للمقارنة. وتسمّى جميع الأنواع الأخرى أنواعاً قابلة للمقارنة. ويمنع المصرِّفون مقارنة قيمتين من نوعين غير قابلين للمقارنة.
لاحظ أن الأنواع غير القابلة للمقارنة تُسمّى أيضاً أنواعاً غير قابلة للمقارنة في مقالات كثيرة.
يجب أن يكون نوع المفتاح لأي نوع خريطة نوعاً قابلاً للمقارنة.
يمكننا تعلّم المزيد عن القواعد التفصيلية للمقارنات من مقالة تحويلات القيم والإسنادات والمقارنات في Go.
حقيقة: البرمجة كائنية التوجه في Go
ليست Go لغة برمجة كائنية التوجه كاملة المزايا، لكن Go تدعم فعلاً بعض عناصر البرمجة كائنية التوجه. يرجى قراءة المقالات المدرجة التالية للتفاصيل:
حقيقة: الأجناس العامة في Go
قبل Go 1.18، كانت الوظائف العامة في Go محدودة بالأنواع والدوال المدمجة. ومنذ Go 1.18، أصبحت الأجناس العامة المخصّصة مدعومة. يرجى قراءة مقالة الأجناس العامة في Go للأجناس العامة المدمجة وكتاب Go Generics 101 للأجناس العامة المخصّصة.