Go में map पर इटरेशन: 5 घातक गलतियाँ और उनसे कैसे बचें

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

Go में map पर इटरेशन: गलतियों से कैसे बचें (और डेटा न खोएं)

\n\n

यदि आपने कभी Go में प्रोग्राम किया है, तो आपने निश्चित रूप से map का सामना किया होगा - जो सबसे शक्तिशाली और धोखेबाज डेटा संरचनाओं में से एक है। लगता है इससे आसान क्या हो सकता है: range लें, कुंजियों पर जाएँ, मान प्राप्त करें। लेकिन शैतान, हमेशा की तरह, विवरण में है। Stack Overflow पर "Iterating over all the keys of a map" प्रश्न ने हज़ारों बार देखा गया है, और यह कोई संयोग नहीं है।

\n\n

आज हम Go में map पर इटरेशन करते समय शीर्ष गलतियों का विश्लेषण करेंगे, जो अनुभवी डेवलपर्स भी करते हैं। आप जानेंगे कि इटरेशन का क्रम एक लॉटरी क्यों है, goroutines में map के साथ सुरक्षित रूप से कैसे काम करें, और यदि map अचानक nil हो जाए तो क्या करें। चलिए शुरू करते हैं!

\n\n

गलती #1: क्रम पर भरोसा करना (या "मेरी कुंजियाँ कहाँ हैं?")

\n\n

सबसे आम जाल यह विश्वास है कि range कुंजियों को उसी क्रम में लौटाएगा जिसमें आपने उन्हें जोड़ा था। Go में ऐसा नहीं है। map पर इटरेशन का क्रम मूल रूप से अनिर्धारित है।

\n\n

इस कोड को देखें:

\n\n
package main\n\nimport "fmt"\n\nfunc main() {\n    m := map[string]int{\n        "apple":  1,\n        "banana": 2,\n        "cherry": 3,\n    }\n\n    for i := 0; i < 3; i++ {\n        fmt.Println("इटरेशन", i+1)\n        for k, v := range m {\n            fmt.Printf("  %s: %d\\n", k, v)\n        }\n    }\n}\n
\n\n

संभवतः, प्रत्येक रन अलग क्रम देगा। यह कोई बग नहीं, बल्कि एक फीचर है: Go जानबूझकर क्रम को रैंडमाइज़ करता है ताकि डेवलपर्स इसके आदी न हों और यादृच्छिक अनुक्रम पर तर्क न बनाएँ।

\n\n

कैसे बचें?

\n
    \n
  • क्रम पर भरोसा न करें - यह मुख्य नियम है।
  • \n
  • यदि क्रम महत्वपूर्ण है, तो कुंजियों को अलग से संग्रहीत करने के लिए []string का उपयोग करें और उन्हें सॉर्ट करें:
  • \n
\n\n
package main\n\nimport (\n    "fmt"\n    "sort"\n)\n\nfunc main() {\n    m := map[string]int{"apple": 1, "banana": 2, "cherry": 3}\n\n    keys := make([]string, 0, len(m))\n    for k := range m {\n        keys = append(keys, k)\n    }\n    sort.Strings(keys)\n\n    for _, k := range keys {\n        fmt.Printf("%s: %d\\n", k, m[k])\n    }\n}\n
\n\n

सुझाव: यदि आपको इन्सर्शन क्रम बनाए रखने की आवश्यकता है, तो github.com/elliotchance/orderedmap लाइब्रेरी पर विचार करें या दो संरचनाओं (map + slice) के आधार पर अपना खुद का रैपर लिखें।

\n\n

गलती #2: एक साथ लिखने के साथ इटरेशन (data race)

\n\n

Go में map समवर्ती पहुँच के लिए सुरक्षित नहीं है। यदि एक goroutine map को पढ़ (इटरेट) रही है, और दूसरी उसी समय उसमें लिख रही है, तो आपको fatal error: concurrent map iteration and map write मिलेगा।

\n\n

घातक त्रुटि का उदाहरण:

\n\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\nfunc main() {\n    m := make(map[int]int)\n\n    go func() {\n        for i := 0; i < 100; i++ {\n            m[i] = i\n        }\n    }()\n\n    go func() {\n        for k, v := range m {\n            fmt.Println(k, v)\n        }\n    }()\n\n    time.Sleep(time.Second)\n}\n
\n\n

यह कोड पैनिक के साथ क्रैश होगा। और हमेशा नहीं - race condition 1000वें रन पर दिखाई दे सकता है।

\n\n

कैसे बचें?

\n\n

तीन सिद्ध तरीके हैं:

\n\n
    \n
  1. sync.Mutex - पहुँच को सिंक्रोनाइज़ करने के लिए क्लासिक म्यूटेक्स:
  2. \n
\n\n
package main\n\nimport (\n    "fmt"\n    "sync"\n)\n\ntype SafeMap struct {\n    mu sync.Mutex\n    m  map[int]int\n}\n\nfunc (s *SafeMap) Set(key, value int) {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    s.m[key] = value\n}\n\nfunc (s *SafeMap) Iterate(f func(key, value int) bool) {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    for k, v := range s.m {\n        if !f(k, v) {\n            break\n        }\n    }\n}\n\nfunc main() {\n    sm := SafeMap{m: make(map[int]int)}\n    sm.Set(1, 10)\n    sm.Set(2, 20)\n\n    sm.Iterate(func(k, v int) b                

ब्लॉग

पुस्तक अनुशंसाएँ