Rust में Borrow एरर क्यों आती है और इसे कैसे ठीक करें? पूरी गाइड

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

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

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

परिचय: जब Rust आपको "समझ नहीं पाता"

\n

आप Rust में कोड लिख रहे हैं। सब कुछ ठीक चल रहा है। कंपाइलर चुप है। आपको लगता है कि आप मेमोरी के राजा हैं। और फिर — धमाका! — यह एरर आती है:

\n
error[E0507]: cannot move out of borrowed content\n  --> src/main.rs:10:9\n   |\n10 |         let value = self.data;\n   |                     ^^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait\n
\n

या इसकी करीबी रिश्तेदार:

\n
error[E0507]: cannot move out of `*self` which is behind a shared reference\n
\n

अगर आप नए हैं, तो यह किसी जादुई श्राप जैसा लगता है। अगर अनुभवी हैं, तो आप जानते हैं कि यह Borrow Checker (उधार जांचकर्ता) का एक और सबक है।

\n

इस लेख में, हम समझेंगे कि ये एरर क्या मतलब रखती हैं, Rust इतना सख्त क्यों है, और — सबसे महत्वपूर्ण — लाइव कोड उदाहरणों के साथ इन्हें कैसे ठीक करें। यह उबाऊ थ्योरी नहीं, बल्कि ownership और borrowing की दुनिया में जीवित रहने की एक प्रैक्टिकल गाइड है।

\n\n

मुख्य बातें: Ownership, Borrowing और "Move"

\n

गहराई में जाने से पहले, Rust के तीन स्तंभों को याद कर लें:

\n
    \n
  • Ownership (स्वामित्व): Rust में हर वैल्यू का एक ही मालिक होता है।
  • \n
  • Borrowing (उधार लेना): आप रेफरेंस (&T — अपरिवर्तनीय, &mut T — परिवर्तनीय) के ज़रिए वैल्यू को "उधार" ले सकते हैं, बिना ownership लिए।
  • \n
  • Move (स्थानांतरण): जब आप किसी वैल्यू को नए वेरिएबल में असाइन करते हैं या बिना & के फंक्शन में भेजते हैं, तो ownership स्थानांतरित हो जाती है। मूल वेरिएबल अमान्य हो जाता है।
  • \n
\n

cannot move out of borrowed content एरर तब आती है जब आप उस वैल्यू को स्थानांतरित करने की कोशिश करते हैं जिसका आपके पास केवल रेफरेंस (&T) है। Rust इसे मना करता है क्योंकि स्थानांतरण के बाद रेफरेंस "लटकता हुआ" (dangling pointer) हो जाएगा। और Rust, जैसा कि आप जानते हैं, ऐसी चीज़ों को माफ नहीं करता।

\n\n

परिदृश्य 1: &self के ज़रिए स्ट्रक्चर के फील्ड को स्थानांतरित करने की कोशिश

\n

सबसे आम मामला। आपके पास एक स्ट्रक्चर है, और आप उसके किसी फील्ड को एक मेथड से वापस करना चाहते हैं जो &self लेता है।

\n\n

एरर वाला कोड

\n
struct User {\n    name: String,\n    age: u32,\n}\n\nimpl User {\n    // name को "हमेशा के लिए" देने की कोशिश\n    fn give_name(&self) -> String {\n        self.name // एरर!\n    }\n}\n\nfn main() {\n    let user = User { name: "Ravi".to_string(), age: 25 };\n    let name = user.give_name();\n    println!("{}", name);\n}\n
\n

कंपाइलर कहेगा:

\n
error[E0507]: cannot move out of `self.name` which is behind a shared reference\n
\n

क्यों? self एक &User है। हम self के मालिक नहीं हैं, हमने इसे केवल उधार लिया है। self.name (टाइप String, जो Copy ट्रेट को इम्प्लीमेंट नहीं करता) को स्थानांतरित करने का मतलब है कि मेथड कॉल के बाद मालिक (user) अपना फील्ड खो देगा। लेकिन रेफरेंस &self अभी भी मौजूद है! Rust हमें इस आपदा से बचाता है।

\n\n

इसे कैसे ठीक करें?

\n

आपके काम के अनुसार तीन मुख्य तरीके हैं।

\n\n

तरीका 1: क्लोनिंग (सबसे आसान, लेकिन हमेशा कुशल नहीं)

\n

अगर आपको वैल्यू चाहिए, रेफरेंस नहीं, और आप कॉपी करने की कीमत चुकाने को तैयार हैं:

\n
fn give_name(&self) -> String {\n    self.name.clone() // काम करता है! एक कॉपी बनाता है।\n}\n
\n

नुकसान: String के लिए clone() हीप पर पूरे डेटा की कॉपी बनाता है। बड़े स्ट्रिंग्स पर यह महंगा हो सकता है।

\n\n

तरीका 2: रेफरेंस वापस करना (सिग्नेचर बदलना)

\n

अगर कॉल करने वाले कोड के लिए फील्ड का रेफरेंस काफी है:

\n
fn give_name(&self) -> &String {\n    &self.name // रेफरेंस वापस करता है, कोई स्थानांतरण नहीं\n}\n
\n

अब हम स्थानांतरित नहीं कर रहे, बल्कि उधार ले रहे हैं। सब ठीक है। कॉल करने वाला कोड &String प्राप्त करेगा।

ब्लॉग

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