Rust में फंक्शन को आर्गुमेंट के रूप में: शुरुआत से विशेषज्ञ तक
\n\nक्या आपने कभी Python या JavaScript में लिखा है और आसानी से एक फंक्शन को दूसरे फंक्शन में पास किया है? और फिर Rust पर आए और एक सामान्य जगह पर अटक गए? परिचित लगता है। सवाल "How do you pass a Rust function as a parameter?" — Stack Overflow पर सबसे लोकप्रिय में से एक है। और यह सही भी है: Rust का टाइप सिस्टम और स्वामित्व कई जटिलताएँ जोड़ता है, लेकिन नई संभावनाएँ भी खोलता है।
\n\nइस लेख में, हम तीन मुख्य दृष्टिकोणों पर चर्चा करेंगे: फंक्शन पॉइंटर्स (fn), क्लोज़र्स (closures) और ट्रेट्स। आप जानेंगे कि कब किसका उपयोग करना है, और Rust में कार्यात्मक शैली से डरना छोड़ देंगे।
यह क्यों महत्वपूर्ण है?
\nफंक्शन को पैरामीटर के रूप में पास करने की क्षमता केवल एक अकादमिक चाल नहीं है। यह इसके लिए आधार है:
\n- \n
- लचीली और पुन: प्रयोज्य लाइब्रेरी बनाना (उदाहरण के लिए, इटरेटर, इवेंट हैंडलर); \n
- "स्ट्रैटेजी" और "ऑब्ज़र्वर" पैटर्न को लागू करना; \n
- बहुत सारे if-else के बिना संक्षिप्त और अभिव्यंजक कोड लिखना। \n
Rust में, डायनामिक भाषाओं के विपरीत, कंपाइलर टाइप और लाइफटाइम की सख्ती से जाँच करता है। इसलिए, आपको स्पष्ट रूप से समझना होगा कि आप क्या पास कर रहे हैं: केवल एक फंक्शन का पता, संदर्भ को कैप्चर करने वाला क्लोज़र, या कुछ अधिक जटिल।
\n\nदृष्टिकोण 1: फंक्शन पॉइंटर — fn
\nसबसे सरल और सीधा तरीका। यदि आपका फंक्शन बाहरी वातावरण से वेरिएबल कैप्चर नहीं करता है (यह "शुद्ध" है), तो आप fn टाइप का उपयोग कर सकते हैं। ध्यान दें: fn एक टाइप है, जबकि Fn, FnMut, FnOnce ट्रेट्स हैं। इन्हें भ्रमित न करें!
fn add_one(x: i32) -> i32 {\n x + 1\n}\n\n// फंक्शन जो दूसरे फंक्शन को स्वीकार करता है\nfn apply_twice(f: fn(i32) -> i32, value: i32) -> i32 {\n f(f(value))\n}\n\nfn main() {\n let result = apply_twice(add_one, 5);\n println!("परिणाम: {}", result); // 7\n}\n\nकब उपयोग करें? जब फंक्शन कंपाइल टाइम पर ज्ञात हो, वेरिएबल कैप्चर न करता हो, और आपको क्लोज़र्स के प्रदर्शन की आवश्यकता न हो। फंक्शन पॉइंटर्स एक zero-cost abstraction हैं, उन्हें एलोकेशन की आवश्यकता नहीं होती।
\n\nfn की सीमाएँ
\nमुख्य नुकसान: आप एक ऐसा क्लोज़र पास नहीं कर सकते जो वेरिएबल कैप्चर करता हो। निम्नलिखित कोड कंपाइल नहीं होगा:
\n\nfn main() {\n let y = 10;\n // क्लोज़र y को कैप्चर करता है\n let closure = |x: i32| -> i32 { x + y };\n // त्रुटि! expected fn item, found closure\n // let result = apply_twice(closure, 5);\n}\n\nइसके लिए Fn, FnMut, FnOnce ट्रेट्स की आवश्यकता होती है।
दृष्टिकोण 2: क्लोज़र्स और ट्रेट्स Fn, FnMut, FnOnce
\nRust एक सिस्टम भाषा है, और इसे पता होना चाहिए कि मेमोरी के साथ क्या हो रहा है। क्लोज़र्स वेरिएबल को तीन तरीकों से कैप्चर कर सकते हैं:
\n- \n
- रेफरेंस द्वारा (&T) — ट्रेट
Fn। इसे जितनी बार चाहें कॉल किया जा सकता है, कैप्चर किए गए डेटा को बदले बिना। \n - म्यूटेबल रेफरेंस द्वारा (&mut T) — ट्रेट
FnMut। इसे कई बार कॉल किया जा सकता है, लेकिन डेटा बदल सकता है। \n - वैल्यू द्वारा (T) — ट्रेट
FnOnce। इसे केवल एक बार कॉल किया जा सकता है, उसके बाद क्लोज़र "खपत" हो जाता है। \n
Fn के साथ उदाहरण
\nfn apply_twice_fn<F>(f: F, value: i32) -> i32\nwhere\n F: Fn(i32) -> i32,\n{\n f(f(value))\n}\n\nfn main() {\n let y = 10;\n let closure = |x| x + y; // y को रेफरेंस द्वारा कैप्चर करता है\n let result = apply_twice_fn(closure, 5);\n println!("Fn: {}", result); // 25\n}\n\nFnMut के साथ उदाहरण
\nfn apply_twice_mut<F>(mut f: F, value: i32) -> i32\nwhere\n F: FnMut(i32) -> i32,\n{\n f(f(value))\n}\n\nfn main() {\n let mut counter = 0;\n let mut closure = |x: i32| -> i32 {\n