परिचय: Rust और Haskell की तुलना क्यों करें?
\n\nयदि आपने कभी Stack Overflow पर "Rust में traits और Haskell में typeclasses के बीच क्या अंतर है?" प्रश्न का उत्तर खोजा है, तो आप जानते हैं: यह विषय बहुत गर्म है। हज़ारों व्यूज़, दर्जनों वोट — डेवलपर समुदाय वास्तव में यह समझना चाहता है कि ये दोनों तंत्र कैसे भिन्न हैं।
\n\nपहली नज़र में, वे भाइयों की तरह दिखते हैं: Rust में traits और Haskell में typeclasses दोनों ही विभिन्न प्रकारों के लिए सामान्य व्यवहार को परिभाषित करने की अनुमति देते हैं। लेकिन यदि आप गहराई से देखें, तो पता चलता है कि उनका दर्शन, कार्यान्वयन और व्यावहारिक अनुप्रयोग अलग-अलग ब्रह्मांड हैं।
\n\nइस लेख में, हम Rust में वास्तविक कोड उदाहरणों (तुलना के लिए Haskell के कुछ अंशों के साथ) पर मुख्य अंतरों का विश्लेषण करेंगे, और अंत में आपको स्पष्ट समझ मिलेगी: traits का उपयोग कब करें, और typeclass-जैसे दृष्टिकोण के बारे में कब सोचना बेहतर है।
\n\nसंक्षिप्त अवलोकन: Rust में traits क्या हैं?
\n\nTraits Rust में उन विधियों के एक सेट की घोषणा करने का एक तरीका है जिन्हें किसी विशिष्ट प्रकार के लिए लागू किया जाना चाहिए। यह सामान्यीकृत प्रोग्रामिंग (generics) और Rust में पॉलीमॉर्फिज्म का आधार है।
\n\nएक सरल उदाहरण: कल्पना करें कि हम चाहते हैं कि विभिन्न प्रकार "नमस्ते" कह सकें।
\n\ntrait Greeter {\n fn greet(&self) -> String;\n}\n\nstruct Person {\n name: String,\n}\n\nimpl Greeter for Person {\n fn greet(&self) -> String {\n format!("नमस्ते, मेरा नाम {} है!", self.name)\n }\n}\n\nstruct Robot {\n model: String,\n}\n\nimpl Greeter for Robot {\n fn greet(&self) -> String {\n format!("बीप-बूप, मॉडल: {}", self.model)\n }\n}\n\nfn say_hello(item: &impl Greeter) {\n println!("{}", item.greet());\n}\n\nfn main() {\n let person = Person { name: "अन्ना".into() };\n let robot = Robot { model: "R2-D2".into() };\n say_hello(&person);\n say_hello(&robot);\n}\n\nयहाँ Greeter एक trait है। Person और Robot प्रकार इसे अपने-अपने तरीके से लागू करते हैं। फ़ंक्शन say_hello किसी भी प्रकार को स्वीकार करता है जो Greeter को लागू करता है। यही traits के माध्यम से पॉलीमॉर्फिज्म है।
संक्षिप्त अवलोकन: Haskell में typeclasses क्या हैं?
\n\nTypeclass Haskell में एक इंटरफ़ेस है जो फ़ंक्शन के एक सेट को परिभाषित करता है जिसे विभिन्न प्रकारों के लिए लागू किया जा सकता है। परिचित लगता है, है ना?
\n\nHaskell में समान उदाहरण इस प्रकार दिखेगा:
\n\nclass Greeter a where\n greet :: a -> String\n\ndata Person = Person { name :: String }\ndata Robot = Robot { model :: String }\n\ninstance Greeter Person where\n greet (Person n) = "नमस्ते, मेरा नाम " ++ n ++ "!"\n\ninstance Greeter Robot where\n greet (Robot m) = "बीप-बूप, मॉडल: " ++ m\n\nsayHello :: Greeter a => a -> IO ()\nsayHello item = putStrLn (greet item)\n\nmain = do\n sayHello (Person "अन्ना")\n sayHello (Robot "R2-D2")\n\nसिंटैक्स अलग है, लेकिन तर्क वही है। और फिर भी Rust में traits और Haskell में typeclasses के बीच अंतर यहीं से शुरू होते हैं।
\n\nमुख्य अंतर #1: मोनोमॉर्फ़ाइज़ेशन बनाम बॉक्स (Box)
\n\nसबसे मौलिक traits और typeclasses के बीच अंतर इस बात में निहित है कि कंपाइलर सामान्यीकृत कोड को कैसे संसाधित करता है।
\n\nRust: मोनोमॉर्फ़ाइज़ेशन
\n\nजब आप fn say_hello(item: &impl Greeter) लिखते हैं, तो Rust कंपाइलर प्रत्येक विशिष्ट प्रकार के लिए फ़ंक्शन की एक अलग प्रतिलिपि बनाता है। इसे मोनोमॉर्फ़ाइज़ेशन कहा जाता है। परिणाम: शून्य-लागत अमूर्तता (zero-cost abstraction) — आप उस चीज़ के लिए भुगतान नहीं करते जिसका आप उपयोग नहीं करते। लेकिन कोड का आकार बढ़ सकता है।
Haskell: बॉक्स (डिफ़ॉल्ट रूप से)
\n\nHaskell में, typeclass को आमतौर पर विधि शब्दकोशों (vtable) के माध्यम से लागू किया जाता है। कंपाइलर एक अंतर्निहित पैरामीटर पास करता है — फ़ंक्शन पॉइंटर्स वाली एक तालिका। इसे बॉक्सिंग कहा जाता है। आप को