لدى Go طريقتان لتعريف متغيّر: var، التي تعمل في أي مكان، و:=، صيغة مختصرة تعمل داخل الدوال وتستنتج النوع.
لكل متغيّر نوع ثابت يُحدَّد وقت الترجمة. عندما تعطي قيمة تستنتج Go النوع منها، فكتابة النوع أيضًا (var city string = "Lisbon") مسموحة لكنها زائدة.
var مقابل :=
var | := | |
|---|---|---|
| أين | مستوى الحزمة وداخل الدوال | داخل الدوال فقط |
| النوع | اختياري؛ ومطلوب عند غياب القيمة | مستنتج دائمًا |
| القيمة الأولية | اختيارية (وإلا فالقيمة الصفرية) | مطلوبة |
| إعادة التعريف | أبدًا | مسموحة إذا كان متغيّر واحد على الأقل في الطرف الأول جديدًا |
الاختيار المعتاد داخل دالة:
x := valueعندما تكون لديك قيمة بداية.var x Tعندما تريد القيمة الصفرية، وهذا يشير إلى "فارغ عن قصد":var buf bytes.Bufferوvar total int.var x T = valueعندما لا يكون النوع المستنتج هو ما تريده، مثلvar ratio float64 = 1(أماratio := 1وحدها فستكونint).
:= خارج دالة خطأ صياغة:
./main.go:5:1: syntax error: non-declaration statement outside function body
القيم الصفرية
المتغيّر المعرّف دون قيمة لا يحمل قيمة عشوائية أبدًا. يحمل القيمة الصفرية لنوعه:
| النوع | القيمة الصفرية |
|---|---|
| الأعداد الصحيحة والعشرية | 0 |
bool | false |
string | "" |
| المؤشرات والشرائح والخرائط والقنوات والدوال والواجهات | nil |
| المصفوفات والبنى | كل عنصر أو حقل بقيمته الصفرية |
الواجهات البرمجية الجيدة في Go مصمّمة لتكون القيمة الصفرية مفيدة: sync.Mutex الصفري غير مقفل، وbytes.Buffer الصفري فارغ وجاهز، وlen وrange وappend كلها تعمل مع شريحة nil. والنوع الذي يجب الانتباه له هو الخريطة: قراءة خريطة nil تعيد قيمًا صفرية، لكن الكتابة فيها تسبّب panic. أنشئ الخرائط بـ make أو بحرفية قبل التخزين فيها.
تعريف عدة متغيّرات
تجمع كتلة var ( ... ) المتغيّرات المرتبطة على مستوى الحزمة. في الإسناد المتعدّد مثل x, y = y, x يُقيَّم كل تعبير في الطرف الثاني قبل إسناد أي شيء، ولهذا يعمل التبديل.
لا تعيد := مع عدة أسماء تعريف أي شيء ما دام اسم واحد على الأقل في الطرف الأول جديدًا. الأسماء الموجودة في النطاق نفسه يُسند إليها ببساطة. وهذا ما يتيح لك إعادة استخدام err:
f, err := os.Open("a.txt") // declares f and err
g, err := os.Open("b.txt") // declares g, assigns to the existing err
إذا لم يكن أي من الأسماء جديدًا فهو خطأ:
./main.go:7:4: no new variables on left side of :=
استخدم = العادية للإسناد إلى متغيّر موجود.
المتغيّرات غير المستخدمة أخطاء
المتغيّر المحلي المعرّف الذي لا يُقرأ أبدًا يوقف البناء:
./main.go:9:2: declared and not used: x
ليس تحذيرًا يمكنك تعطيله. احذف المتغيّر، أو استخدمه، أو أثناء تتبّع الأخطاء أسكته بالمعرّف الفارغ: _ = x. ويتجاهل المعرّف الفارغ أيضًا القيم المعادة التي لا تحتاجها: _, err := fmt.Println("hi").
المتغيّرات على مستوى الحزمة ومعاملات الدوال مستثناة من هذه القاعدة.
النطاق والتظليل
يوجد المتغيّر من تعريفه حتى نهاية الكتلة المحيطة ({ ... }). تنشئ := في كتلة داخلية متغيّرًا جديدًا، حتى لو وُجد متغيّر بالاسم نفسه في الخارج. يُظلَّل الخارجي، ولا يتغيّر:
يطبع البرنامج inside: 42 ثم outside: 0. عرّفت := داخل if كلًا من count وerr كمتغيّرين جديدين في الكتلة الداخلية، لأن أيًا منهما لم يكن موجودًا هناك بعد. يُترجم هذا بلا مشكلة، وهو مصدر كلاسيكي لأخطاء "اختفت قيمتي".
الإصلاح أن تعرّف err أولًا وتسند بـ =:
var err error
count, err = strconv.Atoi(input)
لا يبلغ go vet عن التظليل افتراضيًا. يفعل ذلك المحلّل المنفصل shadow، ويمكن لأدوات الفحص مثل golangci-lint تفعيله.
التسمية
- استخدم
camelCase:maxRetriesوuserID. لاmax_retries. - أبقِ الاختصارات بحالة واحدة:
userIDوhttpClientوparseURL، لاuserId. - أسماء قصيرة للنطاقات القصيرة:
iفي حلقة، وrلقارئ، وerrلخطأ. وأسماء أطول ووصفية للمتغيّرات على مستوى الحزمة. - الاسم الذي يبدأ بحرف كبير على مستوى الحزمة (
var Timeout) مُصدَّر إلى الحزم الأخرى. راجع صفحة الحزم والاستيراد.
المتغيّرات على مستوى الحزمة
المتغيّرات المعرّفة خارج الدوال تعيش طوال البرنامج وتُهيّأ قبل تشغيل main، بترتيب الاعتماديات. وهي مريحة للإعدادات والذاكرات المؤقّتة، وتتشاركها كل الـ goroutines. إذا كتبت أكثر من goroutine في أحدها، فاحمه بـ mutex أو استخدم sync/atomic، وإلا فلديك سباق بيانات.
فضّل الثوابت للقيم التي لا تتغيّر أبدًا. لا يمكن تعديل const بالخطأ ولا يكلّف شيئًا وقت التشغيل.
الأسئلة الشائعة
ما الفرق بين var و:= في Go؟
تعرّف := متغيّرًا وتهيّئه بنوع مستنتج، ولا تعمل إلا داخل الدوال: count := 10. أما var فتعمل في كل مكان (بما في ذلك مستوى الحزمة)، وتتيح لك ذكر النوع، ويمكن أن تحذف القيمة الأولية لتحصل على القيمة الصفرية: var count int. داخل الدوال يستخدم أسلوب Go المعتاد := عندما توجد قيمة أولية وvar عندما تريد القيمة الصفرية.
ما هي القيم الصفرية في Go؟
كل متغيّر يُعرَّف دون قيمة يأخذ القيمة الصفرية لنوعه: 0 للأرقام، وfalse لـ bool، و"" لـ string، وnil للمؤشرات والشرائح والخرائط والقنوات والدوال والواجهات. وتأخذ البنى القيمة الصفرية لكل حقل. لا توجد متغيّرات غير مهيّأة في Go.
كيف أصلح خطأ "declared and not used" في Go؟
ترفض Go متغيّرًا محليًا يُسند إليه ولا يُقرأ أبدًا. استخدمه، أو احذفه، أو أثناء تتبّع الأخطاء أسنده إلى المعرّف الفارغ بـ _ = x. المتغيّرات على مستوى الحزمة ومعاملات الدوال مستثناة.
كيف أعرّف متغيّرًا عامًا في Go؟
استخدم var على مستوى الحزمة، خارج أي دالة: var counter int. الصيغة المختصرة := غير مسموحة هناك. الاسم الذي يبدأ بحرف كبير (var Counter int) مرئي للحزم الأخرى؛ والذي يبدأ بحرف صغير مرئي في حزمته فقط. احمِ المتغيّرات على مستوى الحزمة بـ mutex إذا كانت goroutines تكتب فيها.