مقارنة struct و slice و map في Go: من المبتدئ إلى المحترف
\n\nهل سبق لك أن كتبت كودًا بلغة Go كان من المفترض أن يقارن بين هيكلين (struct)، أو شريحتين (slice)، أو خريطتين (map)، وحصلت على خطأ في الترجمة: invalid operation: cannot compare؟ إذا كان الأمر كذلك، فأنت لست وحدك. هذا أحد أكثر الأسئلة شيوعًا على Stack Overflow (عشرات الآلاف من المشاهدات!)، والذي يحير حتى المطورين ذوي الخبرة الذين انتقلوا إلى Go من لغات أخرى.
في هذه المقالة، سنوضح مرة واحدة وإلى الأبد كيفية مقارنة أنواع البيانات المركبة في Go بشكل صحيح. ستتعرف على سبب عدم عمل == دائمًا، ومتى تحتاج إلى استخدام reflect.DeepEqual، وكيفية كتابة دالة مقارنة فعالة خاصة بك. هيا بنا!
لماذا لا تعمل المقارنة البسيطة باستخدام == دائمًا؟
\n\nفي Go، يعمل العامل == فقط مع الأنواع القابلة للمقارنة (comparable). وتشمل هذه:
- \n
- الأنواع الأساسية:
int،string،bool،float، إلخ. \n - المؤشرات (
pointer) \n - القنوات (
channel) \n - الواجهات (إذا كانت الأنواع الديناميكية قابلة للمقارنة) \n
- الهياكل (struct) التي جميع حقولها من الأنواع القابلة للمقارنة \n
- المصفوفات (
array)، إذا كان عنصر المصفوفة قابلاً للمقارنة \n
أما الشرائح (slice) والخرائط (map) فهي غير قابلة للمقارنة بشكل مباشر باستخدام ==. محاولة القيام بذلك ستؤدي إلى خطأ في الترجمة. لماذا؟ لأن الشريحة هي نوع مرجعي، يتضمن مؤشرًا للبيانات، والطول، والسعة. قد تكون شريحتان "متساويتين" في المحتوى، لكن لهما بنى داخلية مختلفة.
package main\n\nimport "fmt"\n\nfunc main() {\n a := []int{1, 2, 3}\n b := []int{1, 2, 3}\n \n // خطأ في الترجمة!\n // fmt.Println(a == b) \n \n fmt.Println("يعمل فقط مع nil:")\n fmt.Println(a == nil) // false\n fmt.Println(b == nil) // false\n}\n\nنفس الشيء ينطبق على الخرائط. الشيء الوحيد الذي يمكنك التحقق منه باستخدام == هو المساواة مع nil.
مقارنة الهياكل (struct) في Go
\n\nمقارنة الهياكل هي الحالة الأكثر صعوبة. إذا كانت جميع حقول هيكلك من الأنواع القابلة للمقارنة، فإن == يعمل!
type Person struct {\n Name string\n Age int\n}\n\nfunc main() {\n p1 := Person{"Alice", 30}\n p2 := Person{"Alice", 30}\n \n fmt.Println(p1 == p2) // true — يعمل!\n}\n\nلكن المشكلة تأتي عندما يحتوي الهيكل على شريحة أو خريطة:
\n\ntype PersonWithHobbies struct {\n Name string\n Age int\n Hobbies []string // شريحة — نوع غير قابل للمقارنة\n}\n\nfunc main() {\n p1 := PersonWithHobbies{"Bob", 25, []string{"reading", "coding"}}\n p2 := PersonWithHobbies{"Bob", 25, []string{"reading", "coding"}}\n \n // خطأ في الترجمة!\n // fmt.Println(p1 == p2)\n}\n\nفي هذه الحالة، سيعطي مترجم Go خطأ: struct containing []string cannot be compared.
الحل رقم 1: reflect.DeepEqual — شامل لكن بطيء
\n\nأسهل طريقة لمقارنة أي أنواع معقدة هي استخدام الدالة reflect.DeepEqual من المكتبة القياسية للغة Go. وهي تجتاز بشكل متكرر الهياكل والشرائح والخرائط وتقارن محتوياتها.
import (\n "fmt"\n "reflect"\n)\n\nfunc main() {\n // مقارنة الشرائح\n a := []int{1, 2, 3}\n b := []int{1, 2, 3}\n fmt.Println(reflect.DeepEqual(a, b)) // true\n \n // مقارنة الخرائط\n m1 := map[string]int{"a": 1, "b": 2}\n m2 := map[string]int{"a": 1, "b": 2}\n fmt.Println(reflect.DeepEqual(m1, m2)) // true\n \n // مقارنة الهياكل ذات الحقول غير القابلة للمقارنة\n p1 := PersonWithHobbies{"Bob", 25, []string{"reading"}}\n p2 := PersonWithHobbies{"Bob", 25, []string{"reading"}}\n fmt.Println(reflect.DeepEqual(p1, p2)) // true\n}\n\n<