مدونة

بعض التفاصيل الدقيقة (الحقيقية) في Go

قرأت للتو مقالاً: 15 تفصيلاً دقيقاً في Go قد لا تعرفها بالفعل. بعد قراءة ذلك المقال، أشعر أن العنوان غريب/غير مناسب بعض الشيء. لأن كل ما يسمى تفاصيل دقيقة ورد في ذلك المقال هو مما ينبغي لمبرمج Go مؤهل أن يعرفه.

لدى Go بالفعل تفاصيل دقيقة كثيرة. وحتى كثير من مبرمجي Go المحترفين لا يدركون بعضها. وهنا يعرض هذا المقال عدة منها (وكلها من كتاب Go Details & Tips 101).

المحتويات:

  • مقارنة مؤشرين إلى قيم صفرية الحجم قد تكون نتيجتها صحيحة أو خاطئة، حتى أثناء تنفيذ البرنامج نفسه
  • for true {...} و for {...} ليستا متكافئتين
  • عند استخدام دالة make المدمجة لإنشاء خريطة، يمكن أن يكون المعطى الثاني ( cap ) سالباً
  • aConstantString[:] ليست ثابتاً
  • إذا كان المعامَل به الأيسر في تعبير إزاحة بتات غير ثابت غير مُنمَّط، فيُحدَّد نوعه بالنوع المفترض للتعبير

مقارنة مؤشرين إلى قيم صفرية الحجم قد تكون نتيجتها صحيحة أو خاطئة، حتى أثناء تنفيذ البرنامج نفسه

على سبيل المثال. ماذا يطبع البرنامج التالي؟ لا شيء أم true؟


package main

var a, b [0]int
var p, q = &a, &b

func main() {
	if (p == q) {
		p, q = &a, &b
		println(p == q)
	}
}

يطبع false (مع سلسلة أدوات Go الرسمية). مفاجأة؟ لكن يبدو أن السلوك لا يخالف مواصفات Go.

مثال مشابه:


package main

var a, b struct{}
var p, q = &a, &b

func main() {
	var p2, q2 = &a, &b
	println(p == q)   // true
	println(p == p2)  // true
	println(q == q2)  // true
	println(p == q2)  // true
	println(q == p2)  // true
	println(p2 == q2) // false
}

for true {...} وfor {...} ليستا متكافئتين

على سبيل المثال، تُصرَّف الدالة foo الموضحة أدناه، لكن bar لا تُصرَّف.


func foo() int {
  for {}
}

func bar() int {
  for true {}
}

عند استخدام دالة make المدمجة لإنشاء خريطة، يمكن أن يكون المعطى الثاني (cap) سالباً

على سبيل المثال، تُصرَّف الشيفرة التالية.


package main

var n = -100

func main() {
    var m = make(map[int]bool, n)
    m[123] = true;
}

المعطى cap السالب مكافئ لحذفه.

aConstantString[:] ليست ثابتاً

إليك مثالاً يثبت ذلك.


package main

const s = "zigo 101" // len(s) == 8
var a byte = 1 << len(s) / 128
var b byte = 1 << len(s[:]) / 128

func main() {
	println(a, b) // 2 0
}

قد يفاجأ بعض مبرمجي Go بهذه النتيجة. ولمعرفة سبب هذه النتيجة جيداً، نحتاج إلى معرفة التفصيل الدقيق التالي.

إذا كان المعامَل به الأيسر في تعبير إزاحة بتات غير ثابت غير مُنمَّط، فيُحدَّد نوعه بالنوع المفترض للتعبير

على سبيل المثال، يطبع البرنامج التالي 0 0 2.


package main

const N = 8
var n = N

func main() {
	var x byte = 1 << n / 128
	var y = byte(1 << n / 128)
	var z byte = 1 << N / 128

	println(x, y, z) // 0 0 2
}

في التعريفين الأولين، يُفترض أن نوع 1 هو byte في كلٍّ منهما. لذا فكلا التعريفين مكافئ للسطر التالي، حيث يتجاوز تعبير وقت التشغيل byte(1) << n حدوده (ويُقتطع إلى 0).


    ... = byte(1) << n // 128

التعريف الأخير مكافئ للسطر التالي. يُقيَّم التعبير 1 << N / 128 في وقت التصريف. وفي هذا التعبير، يُعامَل 1 كقيمة int غير مُنمَّطة.


   var z = 2