Value receiver बनाम pointer receiver: एक अटूट द्वंद्व
\n\nयदि आपने कभी Go में लिखा है, तो आपने निश्चित रूप से इस प्रश्न का सामना किया होगा। आप Stack Overflow खोलते हैं — और वहाँ हज़ारों व्यूज़, सैकड़ों टिप्पणियाँ, और हर दूसरा नौसिखिया (और केवल वे ही नहीं) अपना दिमाग खपाता है: "मुझे क्या चुनना चाहिए? Value receiver या pointer receiver?"
\n\nस्पॉयलर: सभी स्थितियों के लिए कोई एक सही उत्तर नहीं है। लेकिन कुछ स्पष्ट नियम हैं जो 95% मामलों में आपको गलती करने से बचाएंगे। और आज हम उन्हें हमेशा के लिए समझ लेंगे।
\n\nइस लेख में, मैं केवल सूखे दस्तावेज़ का अनुवाद नहीं करूँगा। हम वास्तविक मामलों पर गौर करेंगे, प्रदर्शन, परिवर्तनशीलता, इंटरफ़ेस और यहाँ तक कि यह भी देखेंगे कि Go समुदाय विभिन्न दृष्टिकोणों को कैसे देखता है। चलिए शुरू करते हैं!
\n\nGo में receiver क्या है?
\n\nउन लोगों के लिए जो भाषा से अभी परिचित हो रहे हैं: receiver एक पैरामीटर है जो एक फ़ंक्शन को एक प्रकार से जोड़ता है। Go में इसी तरह विधियाँ (methods) लागू की जाती हैं।
\n\ntype User struct {\n Name string\n Age int\n}\n\n// Value receiver\nfunc (u User) Greet() string {\n return "नमस्ते, मैं " + u.Name + " हूँ"\n}\n\n// Pointer receiver\nfunc (u *User) Birthday() {\n u.Age++\n}\n\n\nपहले मामले में, हम संरचना की एक प्रतिलिपि के साथ काम कर रहे हैं। दूसरे में, हम मूल पर एक पॉइंटर के साथ काम कर रहे हैं। यह एक छोटी सी बात लगती है। लेकिन इस छोटी सी बात से पूरे आर्किटेक्चरल निर्णय निकलते हैं।
\n\nValue receiver का उपयोग कब करें
\n\nआइए सबसे सरल और सुरक्षित विकल्प से शुरू करते हैं।
\n\n1. विधि प्राप्तकर्ता को नहीं बदलती
\n\nयह सुनहरा नियम है। यदि विधि केवल डेटा पढ़ती है और कुछ नहीं बदलती है — तो value receiver का उपयोग करें। यह सुरक्षित, पूर्वानुमानित और समझने में आसान है।
\n\ntype Rectangle struct {\n Width, Height float64\n}\n\nfunc (r Rectangle) Area() float64 {\n return r.Width * r.Height\n}\n\n\nकोई उम्मीद नहीं करता कि क्षेत्रफल की गणना आयत को ही बदल देगी। यहाँ Value receiver एक आदर्श विकल्प है।
\n\n2. छोटी संरचनाएँ
\n\nयदि आपकी संरचना में कुछ फ़ील्ड हैं (जैसे ऊपर Rectangle), तो प्रतिलिपि बनाने में लगभग कुछ भी खर्च नहीं होता। इसके अलावा, यह पॉइंटर के साथ काम करने से तेज़ हो सकता है, क्योंकि डेटा हीप के बजाय स्टैक पर रहता है।
\n\n3. सुरक्षा और अपरिवर्तनीयता
\n\nकभी-कभी आप जानबूझकर यह गारंटी देना चाहते हैं कि विधि मूल डेटा को नहीं बदलेगी। Value receiver आपको कंपाइलर स्तर पर यह गारंटी देता है।
\n\ntype Config struct {\n Timeout int\n}\n\nfunc (c Config) WithTimeout(t int) Config {\n c.Timeout = t\n return c\n}\n\n\nयह फंक्शनल प्रोग्रामिंग का पैटर्न है: विधि मूल को छुए बिना एक नई प्रतिलिपि लौटाती है। अपरिवर्तनीय कॉन्फ़िगरेशन के लिए आदर्श।
\n\nPointer receiver कब चुनें
\n\nयहाँ सबसे दिलचस्प हिस्सा शुरू होता है। Pointer receiver एक शक्तिशाली उपकरण है, लेकिन इसके साथ सावधानी बरतने की आवश्यकता है।
\n\n1. विधि प्राप्तकर्ता को बदलती है
\n\nयह सबसे स्पष्ट मामला है। यदि आप SetName, UpdateStatus या Increment लिख रहे हैं — तो आपको एक पॉइंटर की आवश्यकता है।
type Counter struct {\n Value int\n}\n\nfunc (c *Counter) Increment() {\n c.Value++\n}\n\n\nपॉइंटर के बिना, आप एक प्रतिलिपि को बढ़ाएंगे, और मूल अपरिवर्तित रहेगा। शुरुआती लोगों की एक क्लासिक गलती।
\n\n2. बड़ी संरचनाएँ
\n\nयदि आपकी संरचना सौ बाइट्स या उससे अधिक है, तो विधि के प्रत्येक कॉल पर प्रतिलिपि बनाना महंगा है। खासकर यदि विधि को लूप में कॉल किया जाता है।
\n\ntype LargeData struct {\n Buffer [1024]byte\n Meta map[string]interface{}\n}\n\n// हर कॉल पर ऐसा कॉपी करना दर्दनाक है\nfunc (l *LargeData) Process() {\n // मूल के साथ काम करें\n}\n\n\nयहाँ pointer receiver केवल सुविधा नहीं है, बल्कि एक प्रदर्शन अनुकूलन है।
\n\n