import में अंडरस्कोर के पीछे क्या छिपा है? उन लोगों के लिए गाइड जो Go को गहराई से समझना चाहते हैं
\n\nक्या आपने कभी Go में किसी और का कोड खोला है और import _ "github.com/lib/pq" जैसी लाइन देखी है? पहली प्रतिक्रिया — "क्या यह बिल्कुल कानूनी है? कंपाइलर त्रुटि नहीं देगा?" स्पॉयलर: यह त्रुटि नहीं देगा। इससे भी बढ़कर, यह भाषा की सबसे सुंदर विशेषताओं में से एक है, जो अक्सर शुरुआती लोगों के मन में सवाल पैदा करती है। आइए समझते हैं कि Go में import से पहले यह रहस्यमयी अंडरस्कोर प्रतीक वास्तव में क्या करता है, और इसे समझदारी से कैसे उपयोग करें।
Stack Overflow पर इस प्रश्न ने हज़ारों व्यूज़ जुटाए हैं — और यह आश्चर्य की बात नहीं है। सिंटैक्स एक टाइपिंग गलती जैसा दिखता है, लेकिन इसके पीछे पैकेज इनिशियलाइज़ेशन और साइड इफेक्ट्स की गहरी अवधारणा है। इस लेख में हम केवल एक सूखी परिभाषा नहीं देंगे, बल्कि वास्तविक परिदृश्य दिखाएंगे जहाँ _ के बिना काम नहीं चलता। चलिए शुरू करते हैं!
समस्या: "इम्पोर्ट किया, लेकिन उपयोग नहीं किया"
\n\nGo में एक सख्त नियम है: प्रत्येक इम्पोर्ट किए गए पैकेज का उपयोग होना चाहिए। अन्यथा कोड कंपाइल नहीं होगा। यह सफाई और प्रदर्शन के लिए किया गया है — कोई मृत वजन नहीं। लेकिन क्या होगा अगर हमें प्रोग्राम शुरू होने पर केवल पैकेज का कोड निष्पादित करना हो, बिना उसके फ़ंक्शन को सीधे कॉल किए? यहाँ blank identifier (खाली पहचानकर्ता) — प्रतीक _ — काम आता है।
जब आप लिखते हैं:
\nimport _ "some/package"\n— तो आप कंपाइलर से कह रहे हैं: "मुझे पता है कि मैं इस पैकेज के फ़ंक्शन को कॉल नहीं करूंगा, लेकिन कृपया इसका init() फ़ंक्शन निष्पादित करें"। Go इनिशियलाइज़ेशन करेगा, लेकिन "unused import" त्रुटि नहीं देगा।
init() कैसे काम करता है और यह क्यों महत्वपूर्ण है
\n\nGo में प्रत्येक पैकेज में एक या अधिक init() फ़ंक्शन हो सकते हैं। वे पैकेज के इम्पोर्ट होने पर, main() फ़ंक्शन से पहले स्वचालित रूप से निष्पादित होते हैं। निष्पादन का क्रम सख्ती से निर्धारित है:
- \n
- पहले पैकेज के वेरिएबल इनिशियलाइज़ होते हैं \n
- फिर
init()कॉल होता है \n - यदि पैकेज अन्य पैकेज इम्पोर्ट करता है — वे पहले इनिशियलाइज़ होते हैं \n
स्पष्टता के लिए उदाहरण। एक config पैकेज बनाते हैं:
// config/config.go\npackage config\n\nvar AppName string\n\nfunc init() {\n AppName = "MyApp v1.0"\n println("Config initialized:", AppName)\n}\n\nअब main.go में हम इस पैकेज को "चुपचाप" इम्पोर्ट कर सकते हैं:
\n// main.go\npackage main\n\nimport (\n _ "example.com/project/config"\n "fmt"\n)\n\nfunc main() {\n fmt.Println("Main function started")\n}\n\nनिष्पादन का परिणाम:
\nConfig initialized: MyApp v1.0\nMain function started\n\nध्यान दें: हमने config.AppName को कॉल नहीं किया — हमने केवल पैकेज को अंडरस्कोर के साथ इम्पोर्ट किया, और इसका init() स्वचालित रूप से चल गया। यह मूल तंत्र है जिस पर कई वास्तविक समाधान बनाए गए हैं।
मुख्य परिदृश्य: डेटाबेस ड्राइवरों का पंजीकरण
\n\nproduction-कोड में import _ का सबसे आम उदाहरण डेटाबेस ड्राइवरों का पंजीकरण है। मानक पैकेज database/sql "फ़ैक्टरी" पैटर्न का उपयोग करता है: सभी ड्राइवर अपने init() फ़ंक्शन में sql.Register() के माध्यम से पंजीकृत होते हैं।
देखें कि PostgreSQL (ड्राइवर lib/pq) कैसे कनेक्ट होता है:
import (\n "database/sql"\n _ "github.com/lib/pq" // "postgres" ड्राइवर को पंजीकृत करता है\n)\n\nfunc main() {\n db, err := sql.Open("postgres", "user=... dbname=...")\n // ...\n}\n\nlib/pq के अंदर init() में कुछ इस प्रकार लिखा होता है:
func init() {\n sql.Register("postgres", &Driver{})\n}\n\nखाली इम्पोर्ट के बिना ड्राइवर पंजीकृत नहीं होता, और sql.Open("postgres", ...) "unknown driver" त्रुटि लौटाता। यही MySQL, SQLite, Oracle और अन्य DB के लिए भी काम करता है।