Skip to content
HI

Rust में Scientific Calculator बनाएँ — Variables और Types समझें

एक simple scientific calculator बनाकर Rust variables और types सीखें।

19 min read

इस लर्निंग रस्ट श्रृंखला में आपका स्वागत है जहां हम प्रति लेख एक प्रोजेक्ट बनाकर रस्ट सीखेंगे और प्रत्येक अवधारणा को काफी गहराई से सीखेंगे। यह मेरी नई रस्ट सीरीज़ है और यह मेरी पिछली रस्ट सीरीज़ की तुलना में कहीं अधिक व्यापक होगी। पहले 5-6 लेखों के लिए, परियोजनाएँ अपेक्षाकृत सरल होंगी लेकिन उसके बाद मैं आपको विश्वास दिलाता हूँ, हम ढेर सारी दिलचस्प परियोजनाएँ बनाने जा रहे हैं और बहुत सी चीजें सीखेंगे। तो, चलिए पहले लेख से शुरू करते हैं

इस लेख में, हम एक सरल वैज्ञानिक कैलकुलेटर बनाकर रस्ट वेरिएबल्स और प्रकारों के बारे में जानने जा रहे हैं।

पूर्वावश्यकताएँ

आपको किसी भी एक प्रोग्रामिंग भाषा को कम से कम उसकी मूल बातें जैसे स्थितियां, चर, लूप, फ़ंक्शन पता होना चाहिए। सबसे महत्वपूर्ण बात, आपके अंदर सीखने और प्रयास करने की इच्छाशक्ति होनी चाहिए। यह एक लंबी लेकिन कठिन यात्रा होने वाली है इसलिए कमर कस लें।

भाग 1 - जंग लगाना

किसी भी अन्य भाषा की तरह, हमें इसका उपयोग करने से पहले इसे इंस्टॉल करना होगा, इसलिए बस आगे बढ़ें और रस्ट इंस्टॉलेशन निर्देश का पालन करें और वापस आएं रस्टअप के साथ रस्ट इंस्टॉल करें

रस्ट स्थापित करने के बाद, एक नया टर्मिनल खोलें और जांचें कि कंपाइलर उपलब्ध है:

rustc --version

आपको इसके समान संस्करण देखना चाहिए:

rustc 1.97.0

सटीक संस्करण इस बात पर निर्भर करेगा कि आप रस्ट कब इंस्टॉल करते हैं।

अब कार्गो की जाँच करें:

cargo --version

आप सोच रहे होंगे कार्गो क्या है? कार्गो रस्ट का बिल्ड सिस्टम और पैकेज मैनेजर है। हम इस पूरी शृंखला में कार्गो का उपयोग करेंगे।

आप रस्टअप की भी जांच कर सकते हैं:

rustup --version

यदि तीनों कमांड काम करते हैं, तो आपका रस्ट इंस्टॉलेशन तैयार है।

कार्गो के साथ शुरुआत करना

इससे पहले कि हम रस्ट सीखना शुरू करें, आइए जल्दी से समझें कि हम इस पूरी श्रृंखला में अपने रस्ट प्रोजेक्ट कैसे बनाएंगे, बनाएंगे और चलाएंगे।

कार्गो क्या है?

कार्गो रस्ट का बिल्ड सिस्टम और पैकेज मैनेजर है। यह प्रोजेक्ट बनाने, कोड संकलित करने, प्रोग्राम चलाने, कोड की जाँच करने और निर्भरता प्रबंधित करने जैसे कार्यों को संभालता है।

आप इस पूरी शृंखला में कार्गो का उपयोग करेंगे, तो आइए बुनियादी आदेशों से परिचित हों।

एक जंग प्रोजेक्ट बनाना

आइए अपना पहला रस्ट प्रोजेक्ट बनाएं।

अपना टर्मिनल खोलें, उस फ़ोल्डर पर जाएँ जहाँ आप अपना प्रोजेक्ट रखना चाहते हैं, और चलाएँ:

cargo new hello-rust

कार्गो hello-rust नामक एक नई निर्देशिका बनाएगा जिसके अंदर एक बुनियादी रस्ट प्रोजेक्ट होगा।

प्रोजेक्ट में जाएँ:

cd hello-rust

यदि आप अपने संपादक में प्रोजेक्ट खोलते हैं, तो आपको इसके समान एक संरचना दिखाई देगी:

hello-rust/
├── Cargo.toml
└── src/
    └── main.rs

आइए समझते हैं कि ये फाइलें क्या हैं।

Cargo.toml प्रोजेक्ट की मेनिफेस्ट फ़ाइल है। इसमें प्रोजेक्ट के बारे में जानकारी शामिल है, जैसे इसका नाम, संस्करण, रस्ट संस्करण और निर्भरताएँ।

src/main.rs बाइनरी रस्ट एप्लिकेशन का प्रवेश बिंदु है। यहीं पर हमारा main फ़ंक्शन लाइव रहेगा।

निर्माण के दौरान निर्भरता को हल करने के बाद कार्गो एक Cargo.lock फ़ाइल भी बनाता है। आपको अभी इसके बारे में चिंता करने की ज़रूरत नहीं है; हम श्रृंखला में बाद में निर्भरता प्रबंधन पर चर्चा करेंगे।

प्रोजेक्ट चलाना

src/main.rs खोलें। कार्गो हमारे लिए एक छोटा हैलो वर्ल्ड प्रोग्राम तैयार करता है:

fn main() {
    println!("Hello, world!");
}

हम प्रोग्राम को इसके साथ चला सकते हैं:

cargo run

आपको देखना चाहिए:

Hello, world!

cargo run यदि आवश्यक हो तो पहले प्रोजेक्ट को संकलित करता है और फिर परिणामी निष्पादन योग्य चलाता है।

अपना कोड जाँच रहा है

एक अन्य उपयोगी आदेश है:

cargo check

cargo check जाँचता है कि क्या आपका प्रोजेक्ट अंतिम निष्पादन योग्य तैयार किए बिना संकलित होता है।

यह विकास करते समय उपयोगी है क्योंकि यह आपको पूर्ण निर्माण किए बिना तुरंत जांचने देता है कि आपका कोड वैध है या नहीं।

आप मुझे इस पूरी शृंखला में बार-बार cargo check का उपयोग करते हुए देखेंगे।

अपना प्रोजेक्ट बनाना

प्रोजेक्ट को संकलित करने के लिए, आप इसका उपयोग कर सकते हैं:

cargo build

डिफ़ॉल्ट रूप से, यह एक डिबग बिल्ड बनाता है। जब आप अंततः एक अनुकूलित रिलीज़ बिल्ड चाहते हैं, तो आप इसका उपयोग कर सकते हैं:

cargo build --release

अभी के लिए, आपको डिबग और रिलीज़ बिल्ड के बीच अंतर के बारे में चिंता करने की ज़रूरत नहीं है। जब यह प्रासंगिक हो जाएगा तो हम उस पर वापस आएंगे।

कार्गो वर्कफ़्लो

इस बिंदु पर, आपको जो बुनियादी वर्कफ़्लो याद रखना होगा वह है:

cargo new       → create a new project
cargo check     → check that the code compiles
cargo run       → compile and run the program
cargo build     → compile the project

अभी के लिए इतना ही पर्याप्त कार्गो ज्ञान है।

हम श्रृंखला में बाद में कार्गो, निर्भरता, कार्यस्थान, बिल्ड और प्रोजेक्ट कॉन्फ़िगरेशन के बारे में अधिक जानेंगे जब हमें वास्तव में उनकी आवश्यकता होगी।

अब जब हम जानते हैं कि रस्ट प्रोजेक्ट कैसे बनाया और चलाया जाता है, तो आइए रस्ट सीखना शुरू करें।

चर

सबसे पहली चीज़ जो हम सीखेंगे वह है रस्ट में वेरिएबल्स। सिंटैक्स अन्य प्रोग्रामिंग भाषाओं के समान है लेकिन फिर भी कुछ अंतर हैं जिन्हें आपको शुरू से ही जानना और समझना चाहिए।

एक वेरिएबल बनाना

आइए एक वेरिएबल बनाने का तरीका सीखकर शुरुआत करें:

fn main() {
	let number = 10;
	
	println!("{number}")
}

आप इसे अपने टर्मिनल में निम्नलिखित टाइप करके चला सकते हैं:

cargo run

आपको आउटपुट 10 देखना चाहिए। लाइन let number = 10; एक वैरिएबल बाइंडिंग बनाती है। आइए मैं आपको निम्नलिखित छवि के साथ दिखाता हूँ:

छवि 1

रस्ट शब्दावली में, let को एक नाम और एक मान के बीच बाध्यकारी बनाने के रूप में सोचना उपयोगी है। यहां, हम मूल रूप से रस्ट को बता रहे हैं कि नाम number मान 10 को संदर्भित करता है। अभी प्रकारों के बारे में चिंता न करें, हम शीघ्र ही उनके बारे में जानेंगे

अब आप सोच रहे होंगे कि ये तो जावास्क्रिप्ट और पाइथॉन जैसी भाषाओं से काफी मिलती-जुलती लगती है, तो फिर मैं किस अंतर की बात कर रहा था। आइए मैं आपको यह समझाकर दिखाता हूं कि बाइंडिंग से जुड़ी जानकारी के कौन से हिस्से हैं: छवि 2 अभी के लिए, आपको बस यह नोट करना होगा कि बाइंडिंग का एक नाम है जिसे number कहा जाता है और यह एक मान को संदर्भित करता है 10, मान का एक प्रकार है, बाइंडिंग डिफ़ॉल्ट रूप से अपरिवर्तनीय है और बाइंडिंग एक विशेष दायरे में मौजूद है। हम बाद में इस खंड में और अगले खंड में भी इन संपत्तियों पर वापस लौटेंगे।

वेरिएबल डिफ़ॉल्ट रूप से अपरिवर्तनीय हैं

आइए इस प्रोग्राम को आज़माएँ और चलाएँ:

fn main() {
    let number = 10;

    number = 20;

    println!("{number}");
}

आपको यह कहते हुए एक त्रुटि दिखाई देगी कि हम एक अपरिवर्तनीय चर को निर्दिष्ट नहीं कर सकते।

आपको यह याद रखना होगा: let के साथ बनाई गई बाइंडिंग डिफ़ॉल्ट रूप से अपरिवर्तनीय हैं

यदि हम बाइंडिंग से जुड़े मूल्य को बदलना चाहते हैं, तो हमें स्पष्ट रूप से कहना होगा कि यह परिवर्तनशील है और हम कीवर्ड mut का उपयोग करके ऐसा करते हैं:

fn main() {
    let mut number = 10;

    number = 20;

    println!("{number}");
}

अब cargo run का उपयोग करके प्रोग्राम चलाएं और आपको आउटपुट 20 दिखाई देगा।

अभी के लिए, वह अंतर ही काफी है। स्वामित्व और उधार श्रृंखला में बाद में इस अंतर को और अधिक महत्वपूर्ण बना देंगे।

पुनर्नियुक्ति

आइए इस प्रोग्राम को आज़माएँ और चलाएँ:

fn main() {
	let mut number = 10l;
	
	number = 20;
	number = 30;
	
	println!("{number}")
}

आपको आउटपुट 30 मिलेगा। हम पहले 10 मान के साथ बाइंडिंग number बनाते हैं, फिर 20 को पुन: असाइन करते हैं और फिर 30 को पुन: असाइन करते हैं और अंत में number प्रिंट करते हैं।

###छाया रस्ट में, हम उसी नाम का उपयोग करके एक और बाइंडिंग घोषित कर सकते हैं, जैसे:

fn main() {
	let number = 10;
	let number = 20;
	
	println!("{number}")
}

अब, आउटपुट चलाएं और जांचें, आपको आउटपुट 20 मिलेगा। प्रथम दृष्टया, यह पुनर्नियुक्ति जैसा लग सकता है लेकिन ऐसा नहीं है। इसे हम shadowing कहते हैं। पुनर्असाइनमेंट और शैडोइंग के बीच एक सूक्ष्म अंतर है, शैडोइंग में आप देख सकते हैं कि दूसरा let एक नया बाइंडिंग बनाता है जिसका नाम पिछले बाइंडिंग के समान है। पुन:असाइनमेंट में, हम एक ही नाम से एकाधिक बाइंडिंग नहीं बनाते हैं।

छाया देने से प्रकार भी बदल सकता है:

fn main() {
	let value = 10;
	let value = "ten";
	
	println!("{value}")
}

यदि आप इसे चलाएंगे, तो आप देख सकते हैं कि आउटपुट ten होगा। यदि आप पुनर्असाइनमेंट का उपयोग करके ऐसा करेंगे, तो यह विफल हो जाएगा और आपको एक त्रुटि दिखाएगा:

fn main() {
	let mut value = 10;
	value = "ten";
	
	println!("{value}")
}

इस छायांकन अवधारणा का उपयोग बाद में हमारी श्रृंखला में किया जाएगा, इसलिए अभी मैं इस अवधारणा को यहीं छोड़ रहा हूं और इसके बारे में और भी अधिक जानने के लिए हम इसे बाद के लेख में उठाएंगे।

दायरा

इन बंधनों का भी एक दायरा होता है. स्कोप प्रोग्राम का वह क्षेत्र है जिसमें बाइंडिंग का उपयोग किया जा सकता है। मैं आपको एक उदाहरण के साथ दिखाता हूँ:

fn main() {
	let outer = 10;
	
	{
		let inner = 20;
		println!("{outer}")
		println!("{inner}")
	}
	
	println!("{outer}")
}

मुझे लगता है कि आपने इस तरह की गुंजाइश अन्य भाषाओं में भी देखी होगी। आंतरिक बाइंडिंग स्कोप केवल नेस्टेड ब्लॉक के अंदर मौजूद होता है और बाहरी बाइंडिंग स्कोप प्रोग्राम के अंत तक मौजूद होता है। इसलिए जब आप प्रोग्राम चलाते हैं, एक बार जब हमारा प्रोग्राम नेस्टेड ब्लॉक से बाहर हो जाता है, तो inner बाइंडिंग उपलब्ध नहीं रहती है। इसीलिए यदि आप उस नेस्टेड ब्लॉक के बाहर inner बाइंडिंग वैल्यू प्रिंट करने का प्रयास करेंगे, तो आपको एक त्रुटि मिलेगी। आइए मैं आपको इस दृश्य के साथ हमारे उपरोक्त कोड का दायरा दिखाता हूं: छवि 3 हम बाद में स्कोपिंग के विचार को स्वामित्व और जीवनकाल के साथ समझेंगे और जोड़ेंगे, लेकिन अभी के लिए, बस इतना समझें कि स्कोप एक ऐसा क्षेत्र है जिसमें एक बंधन उपलब्ध है

मूल्य वास्तव में कहाँ रहता है?

मैं जानता हूं कि आप सोच रहे होंगे कि जब हम रस्ट प्रोग्राम में let number = 10; लिखते हैं तो वास्तव में क्या होता है। मैं अभी आपको एक संक्षिप्त स्पष्टीकरण दे सकता हूं लेकिन हम इस संबंध में बाद के लेख में गहराई से विचार करेंगे।

इसलिए, पूर्णांक जैसे सरल मानों के लिए, कंपाइलर आम तौर पर मशीन स्टोरेज में सीधे मूल्य का प्रतिनिधित्व अक्सर रजिस्टर या स्टैक मेमोरी में कर सकता है, जो इस पर निर्भर करता है कि जेनरेट किए गए कोड को कैसे अनुकूलित और उपयोग किया जाता है।

वेरिएबल हमेशा स्टैक पर लाइव नहीं होते हैं। कृपया उपरोक्त संक्षिप्त कथन को थोड़ा सा नमक के साथ लें क्योंकि मैं इसे बहुत सरल बना रहा हूँ। जैसा कि मैंने कहा, हम इसके बारे में एक अलग लेख में विस्तार से जानेंगे, इसलिए यह बहुत अच्छा होगा यदि आप धैर्य रखें और अभी केवल भाषा स्तर के व्यवहार को सीखने पर ध्यान केंद्रित करें और मशीन स्तर के विवरण पर ध्यान केंद्रित न करें।

एक आखिरी प्रयोग

आइए हमने जो सीखा है उसे इस कार्यक्रम के साथ जोड़ें:

fn main() {
	let number = 10;
	
	{
		let mut number = number;
		
		number = 20;
		
		println!("{number}");
	}
	
	println!("{number}")
}

मैं कहूंगा कि प्रोग्राम चलाने और आउटपुट की जांच करने से पहले आउटपुट का अनुमान लगाने का प्रयास करें। सबसे पहले, हमने एक बाहरी बाइंडिंग बनाई number और मान है 10, फिर नेस्टेड ब्लॉक के अंदर, हमने उसी नाम से एक आंतरिक बाइंडिंग बनाई, यह बाहरी number बाइंडिंग को छाया देगा और इस आंतरिक बाइंडिंग का मान 10 है, फिर हम मान को पुन: असाइन कर रहे हैं 20(हम इनर बाइंडिंग में mut के कारण ऐसा कर सकते हैं), फिर number को प्रिंट करने पर हमें 20 मिलेगा। अंत में हम नेस्टेड ब्लॉक से बाहर मौजूद हैं और number को फिर से प्रिंट करते हैं लेकिन इस बार यह बाहरी number बाइंडिंग को इंगित करेगा, इसलिए मान 10 को प्रिंट करेगा।

मुझे लगता है, अब तक आपको बुनियादी चर मॉडल के साथ सहज हो जाना चाहिए। अब, हम प्रकारों पर विचार कर सकते हैं

प्रकार

कुछ अन्य भाषाओं की तरह, हमारे पास रस्ट में भी प्रकार हैं। आइए उसी उदाहरण का उपयोग करें जिसका हमने पहले उपयोग किया था और स्वयं मूल्य को देखें:

let number = 10;

हम जानते हैं कि number का तात्पर्य 10 से है, लेकिन सवाल यह है कि यदि 10 है तो किस प्रकार का मूल्य? यह वह जगह है जहां हम शेष अनुभाग पर ध्यान केंद्रित करेंगे जहां हम उन सभी सामान्य प्रकारों को समझेंगे जो रस्ट प्रदान करता है।

सामान्य तौर पर, एक प्रकार हमें बताता है कि वह किस प्रकार के मूल्य के साथ काम कर रहा है और उस मूल्य के लिए कौन से ऑपरेशन मान्य हैं।

आइए एक बहुत ही बुनियादी उदाहरण से शुरुआत करें:

fn main() {
	let number = 10;
	
	println!("{number}")
}

एक बार जब आप इसे चलाएंगे, तो आपको आउटपुट 10 दिखाई देगा। हम यह पहले से ही जानते हैं, इसमें कोई आश्चर्य की बात नहीं है। अब, आइए इसके प्रकार को स्पष्ट करें:

fn main() {
	let number: i32 = 10;
	
	println!("{number}")
}

यह फिर से 10 आउटपुट करेगा लेकिन इस प्रोग्राम में, हमने स्पष्ट रूप से उल्लेख किया है कि मान किस प्रकार का होना चाहिए।

किसी मान के प्रकार का स्पष्ट रूप से उल्लेख करने के लिए आप जिस सिंटैक्स का अनुसरण करेंगे वह यह है:

let name: Type = value;

तो हमारे मूल उदाहरण में, number बाध्यकारी नाम है, i32 प्रकार है और 10 मान है

एक प्रकार वास्तव में क्या निर्धारित करता है?

एक प्रकार चीजों को निर्धारित करता है जैसे कि किन मूल्यों का प्रतिनिधित्व किया जा सकता है, उन मूल्यों पर संचालन कैसे व्यवहार करते हैं, कौन से संचालन वैध हैं, मूल्य अन्य मूल्यों के साथ कैसे बातचीत कर सकते हैं, कंपाइलर रनटाइम पर मूल्य का प्रतिनिधित्व कैसे करता है और कुछ और हो सकता है लेकिन मुझे लगता है कि ये सबसे आम चीजें हैं जिन्हें आपको जानना चाहिए उदाहरण के लिए, पूर्णांक संचालन अंकगणितीय संचालन का समर्थन करते हैं:

fn main() {
	let a: i32 = 10;
	let b: i32 = 20;
	
	let result = a + b;
	
	println!("{result}");
}

यह काम करता है क्योंकि i32 जोड़ने का समर्थन करता है। लेकिन रस्ट असंबंधित प्रकारों के मनमाने मूल्यों को संयोजित करने की अनुमति नहीं देता है। उदाहरण के लिए:

fn main() {
	let a: i32 = 10;
	let b: f64 = 20.5;
	
	let result = a + b;
	
	println!("result")
}

यदि आप इसे चलाएंगे, तो यह विफल हो जाएगा क्योंकि रस्ट को दो अलग-अलग प्रकार दिखाई देंगे i32 और f64 और कंपाइलर यह कहते हुए शिकायत करेगा कि दोनों एक ही प्रकार के नहीं हैं इसलिए ऑपरेशन नहीं कर सकते। हम बाद में देखेंगे कि इस ऑपरेशन को करने के लिए किसी प्रकार को कैसे परिवर्तित किया जाए, लेकिन अभी के लिए बस याद रखें कि विभिन्न प्रकारों पर संचालन करना रस्ट में विफल हो जाएगा।

जंग के अदिश प्रकार

रस्ट में, हमारे पास कुछ आदिम अदिश प्रकार हैं। चार मुख्य श्रेणियाँ हैं:

अदिश प्रकार
पूर्णांक
फ़्लोटिंग-पॉइंट नंबर
बूलियन्स
अक्षर

पूर्णांक और फ़्लोटिंग-पॉइंट श्रेणियों में कई ठोस प्रकार होते हैं। आइए इनमें से प्रत्येक प्रकार पर गौर करें और एक-एक करके इनमें से प्रत्येक प्रकार के बारे में थोड़ा और समझें

हस्ताक्षरित पूर्णांक

एक हस्ताक्षरित पूर्णांक सकारात्मक और नकारात्मक दोनों पूर्ण संख्याओं का प्रतिनिधित्व कर सकता है। रस्ट में, हमारे पास हस्ताक्षरित पूर्णांकों के कई प्रकार हैं, ये प्रकार बिट्स में पूर्णांक के आकार का प्रतिनिधित्व करते हैं।

हस्ताक्षरित पूर्णांक
i8
i16
i32
i64
i128
isize

हमारे पास हस्ताक्षरित पूर्णांकों के विभिन्न प्रकार हैं क्योंकि कार्यक्रमों को कभी-कभी विभिन्न संख्यात्मक श्रेणियों की आवश्यकता होती है। उदाहरण के लिए, i8 में 8 बिट हैं और -128 through 127 का प्रतिनिधित्व कर सकते हैं जबकि i32 में 32 बिट हैं और -2,147,483,648 through 2,147,483,647 का प्रतिनिधित्व कर सकते हैं और i64 इससे भी अधिक का प्रतिनिधित्व कर सकते हैं। मुझे आशा है कि आपको यह विचार मिल गया होगा।

अहस्ताक्षरित पूर्णांक

एक अहस्ताक्षरित पूर्णांक (हाँ, आपने सही अनुमान लगाया) नकारात्मक मानों का प्रतिनिधित्व नहीं कर सकता। उदाहरण के लिए:

fn main() {
	let count: u32 = 100;
	
	println!("{count}");
}

यह बिल्कुल ठीक काम करेगा लेकिन यदि आप उस 100 को इस तरह किसी भी नकारात्मक संख्या से बदल देंगे:

fn main() {
	let count: u32 = -100;
	
	println!("{count}")
}

अब, यदि आप इसे चलाने का प्रयास करेंगे, तो कंपाइलर शिकायत करेगा क्योंकि u8 की सीमा 0 through 255 है और इसीलिए नकारात्मक संख्याएँ मान्य नहीं हैं।

usize और isize

दो पूर्णांक प्रकार हैं जिन पर मैं चाहता हूं कि आप अपना विशेष ध्यान दें: usize और isizei32 या u64 के विपरीत, उनका आकार लक्ष्य वास्तुकला पर निर्भर करता है। उदाहरण के लिए, 64-बिट सिस्टम पर: usize 64 बिट है और isize 64 बिट है लेकिन 32-बिट सिस्टम पर: usize 32 बिट है और isize 32 बिट है। usize का उपयोग आम तौर पर आकार और अनुक्रमणिका के साथ काम करते समय किया जाता है। जब हम संग्रहों के साथ काम करेंगे तो आप अक्सर बाद में इसे देखेंगे। हम बाद के लेख में usize का उपयोग देखेंगे, अभी मैं बस यही चाहता हूं कि आप इसका सार समझें और मुझे आशा है कि आप ऐसा करेंगे।

फ़्लोटिंग पॉइंट प्रकार

जैसा कि हम एक लघु वैज्ञानिक कैलकुलेटर का निर्माण कर रहे हैं, हमें स्पष्ट रूप से दशमलव मान समर्थन की आवश्यकता है और इसका समर्थन करने के लिए रस्ट दो फ़्लोटिंग-पॉइंट प्रकार प्रदान करता है: f32 और f64 आइए मैं आपको फ़्लोटिंग पॉइंट के लिए एक बुनियादी उदाहरण भी दिखाता हूँ:

fn main() {
	let a: f32 = 10.5;
	let b: f64 = 20.5;
	
	println!("{a}");
	println!("{b}");
}

बूलियन

रस्ट बूलियन प्रकार का भी समर्थन करता है और इसे bool के साथ दर्शाया जाता है। इसके बिल्कुल दो संभावित मान हैं: true और false। मैं आपको एक बुनियादी उदाहरण देता हूँ:

fn main() {
	let is_running: bool = true;
	let is_finished: bool = false;
	
	println!("{is_running}");
	println!("{is_finished}");
}

चरित्र

रस्ट वर्ण प्रकार का भी समर्थन करता है और इसे char कीवर्ड द्वारा दर्शाया जाता है। एक char एकल यूनिकोड स्केलर मान का प्रतिनिधित्व करता है। उदाहरण के लिए:

fn main() {
	let letter: char = 'R';
	let symbol: char = '😀';
	
	println!("{letter}");
	println!("{symbol}");
}

याद रखें, जब हम किसी चरित्र को परिभाषित कर रहे होते हैं तो हम सिंगल कोट्स का उपयोग कर रहे होते हैं। यदि आप दोहरे उद्धरण चिह्नों का उपयोग करेंगे, तो यह एक स्ट्रिंग अक्षरशः बन जाएगा। ये दोनों अलग-अलग प्रकार के हैं.

तो, उपरोक्त उदाहरण से, आप देख सकते हैं कि एक रस्ट char यूनिकोड स्केलर मानों का प्रतिनिधित्व कर सकता है और यही कारण है कि आपको char को “एक बाइट” के रूप में नहीं सोचना चाहिए। रस्ट का char एक चार-बाइट प्रकार है जो यूनिकोड स्केलर मान का प्रतिनिधित्व करता है।

प्रकार अनुमान

शुरुआत में, हम अपने वेरिएबल्स के लिए प्रकार नहीं लिख रहे थे लेकिन हाल ही में हमने अपने वेरिएबल्स के प्रकारों का स्पष्ट रूप से उल्लेख करना शुरू कर दिया है। इसका मतलब है कि, रस्ट को हमें प्रत्येक वेरिएबल को एनोटेट करने की आवश्यकता नहीं है, रस्ट प्रकार का अनुमान लगाएगा और इसे प्रकार अनुमान कहा जाता है। प्रकार अनुमान का मतलब यह नहीं है कि एक चर का कोई प्रकार नहीं है। उस चर में अभी भी एक ठोस संकलन-समय प्रकार है। यह सिर्फ इतना है कि रस्ट ने हमारे लिए वह प्रकार निर्धारित कर दिया है। हम इस सिद्धांत को एक उदाहरण से भी सत्यापित कर सकते हैं:

fn main() {
	let number = 10;
	
	println!("{}", std::any::type_name_of_val(&number));
}

आपको आउटपुट को i32 के रूप में देखना चाहिए।

संख्यात्मक शाब्दिक प्रत्यय

जंग हमें संख्यात्मक शाब्दिक में एक प्रकार प्रत्यय संलग्न करने की अनुमति देता है। यह प्रकार को स्पष्ट करने का एक और तरीका है।

fn main() {
	let a = 10_i32;
	let b = 10_i64;
	let c = 10.5_f64;
}

प्रत्यय रस्ट को बताता है कि शाब्दिक किस प्रकार का होना चाहिए। तो, 10_i64 का अर्थ है कि पूर्णांक शाब्दिक 10 का प्रकार i64 होना चाहिए। पहला तरीका जो हमने सीखा वह बाइंडिंग पर टाइप एनोटेशन का उपयोग करता है और यह शाब्दिक पर टाइप प्रत्यय का उपयोग करता है।

जंग स्पष्ट रूप से संख्यात्मक प्रकार को परिवर्तित नहीं करता है

आइए उस उदाहरण पर वापस जाएं जिसका हमने पहले उपयोग किया था:

fn main() {
	let a: i32 = 10;
	let b: f64 = 20.5;
	
	let result = a + b;
	
	println!("{result}");
}

यदि आप इसे चलाएंगे, तो यह विफल हो जाएगा क्योंकि आप दो अलग-अलग प्रकारों के साथ ऑपरेशन करने का प्रयास कर रहे हैं। इसे सफलतापूर्वक चलाने के लिए, हमें इन दो प्रकारों में से एक को परिवर्तित करना होगा और दोनों प्रकारों को समान बनाना होगा:

fn main() {
	let a: i32 = 10;
	let b: f64 = 20.5;
	
	let result = a as f64 + b;
	
	println!("{result}");
}

उस as f64 के साथ, हमने पूर्णांक a को f64 में बदल दिया और अब ऑपरेशन हो सकता है। हम श्रृंखला में बाद में प्रकार रूपांतरण के बारे में अधिक जानेंगे।

वैज्ञानिक कैलकुलेटर का निर्माण

अब से, हम क्रमिक रूप से अपना कैलकुलेटर बनाने जा रहे हैं। मुझे आशा है कि आपने रस्ट पहले ही इंस्टॉल कर लिया है, यदि नहीं तो कृपया इस लेख के शीर्ष पर जाएं और रस्ट इंस्टॉलेशन लिंक प्राप्त करें और निर्देशों का पालन करें। एक बार पूरा हो जाने पर, वापस आएं और इस परियोजना पर काम करना शुरू करें।

प्रोजेक्ट बनाएं

बस अपना टर्मिनल खोलें, अपने पसंदीदा फ़ोल्डर पर जाएं जहां आप यह प्रोजेक्ट बनाना चाहते हैं और फिर एक नया कार्गो प्रोजेक्ट बनाएं:

cargo new scientific-calculator

फिर इस प्रोजेक्ट को अपनी पसंदीदा आईडीई में खोलें। मैं जेड आईडीई का उपयोग करूंगा एक बार जब आप प्रोजेक्ट खोलेंगे, तो आपको यह फ़ोल्डर संरचना दिखाई देगी:

scientific-calculator/
├── Cargo.toml
└── src/
    └── main.rs

इस प्रोजेक्ट के लिए, हमें Cargo.toml को संशोधित करने की आवश्यकता नहीं है क्योंकि हम केवल रस्ट की मानक लाइब्रेरी का उपयोग करेंगे

अब, src/main.rs खोलें और आप देखेंगे कि एक कार्गो जेनरेटेड हैलो वर्ल्ड प्रोग्राम है, आइए उसे बदलें और Scientific Calculator प्रिंट करें:

fn main() {
	println!("Scientific Calculator");
}

एक बार जब आप इसे cargo run के साथ चलाएंगे, तो आपको अपने टर्मिनल पर Scientific Calculator देखना चाहिए।

चार बुनियादी ऑपरेशन जोड़ें

आइए सभी चार बुनियादी अंकगणितीय संक्रियाओं को जोड़ें:

fn main() {
    let a: f64 = 20.0;
    let b: f64 = 6.0;

    let addition = a + b;
    let subtraction = a - b;
    let multiplication = a * b;
    let division = a / b;

    println!("Addition: {addition}");
    println!("Subtraction: {subtraction}");
    println!("Multiplication: {multiplication}");
    println!("Division: {division}");
}

यदि आप इसे चलाएंगे, तो आप देखेंगे कि सभी ऑपरेशन ठीक से काम कर रहे हैं लेकिन विभाजन के लिए फ़ॉर्मेटिंग गायब है। हम उससे बाद में निपटेंगे.

हमारा पहला वैज्ञानिक ऑपरेशन जोड़ें

आइए वर्गमूल, घात, त्रिकोणमितीय संक्रियाएँ जोड़ें:

fn main() {
    let a: f64 = 20.0;
    let b: f64 = 6.0;

    let addition = a + b;
    let subtraction = a - b;
    let multiplication = a * b;
    let division = a / b;

    let square_root = 25.0_f64.sqrt();
    let power = 2.0_f64.powi(8);
    let sine = 0.0_f64.sin();
    let cosine = 0.0_f64.cos();

    println!("Addition: {addition}");
    println!("Subtraction: {subtraction}");
    println!("Multiplication: {multiplication}");
    println!("Division: {division}");
    println!("Square root: {square_root}");
    println!("Power: {power}");
    println!("Sine: {sine}");
    println!("Cosine: {cosine}");
}

अब, यह थोड़ा बेहतर दिख रहा है लेकिन अभी भी एक समस्या है, इनपुट हार्डकोडेड हैं, हमें उपयोगकर्ता से इनपुट लेने का एक तरीका चाहिए। आइए आगे उस पर काम करें।

कमांड लाइन तर्क पढ़ना

उपयोगकर्ता इनपुट को पढ़ने के लिए, हम कमांड लाइन तर्क पढ़ सकते हैं और रस्ट एक प्रोग्राम में पारित तर्कों तक पहुंचने के लिए std::env::args प्रदान करता है। फिलहाल, आइए संख्याओं को पार्स करने या गणना करने के बारे में चिंता न करें। आइए पहले देखें कि हम कमांड लाइन के माध्यम से तर्कों से क्या प्राप्त कर रहे हैं: पूरा main.rs कोड टिप्पणी करें और फिर यह कोड लिखें:

fn main() {
	let args = std::env::args();

    println!("{:?}", args);
}

अब चलाएँ:

cargo run -- add 10 20

आपको प्रोग्राम में पारित तर्कों का प्रतिनिधित्व करने वाला आउटपुट देखना चाहिए। हालाँकि, args एक पुनरावर्तक है, और इसे सीधे प्रिंट करना हमारे उद्देश्यों के लिए विशेष रूप से उपयोगी नहीं है। आइए तर्कों को एक वेक्टर में एकत्रित करें:

अगर आप इटरेटर या वेक्टर जैसी चीजों को नहीं समझते हैं तो चिंता न करें, हम इनके बारे में बाद में सीखेंगे। यहां, हम केवल उपयोगकर्ता इनपुट प्राप्त करने के लिए उनका उपयोग कर रहे हैं। इस लेख का मुख्य लक्ष्य आपको चर और प्रकारों के बारे में समझाना है और इस बिंदु पर यह पहले ही हो चुका है।

fn main() {
    let args: Vec<String> = std::env::args().collect();

    println!("{args:?}");
}

चलाएँ:

cargo run -- add 10 20

आपको कुछ ऐसा ही देखना चाहिए:

["target/debug/scientific-calculator", "add", "10", "20"]

सटीक पहला मान आपके परिवेश पर निर्भर करेगा।

महत्वपूर्ण हिस्सा यह है:

"add"
"10"
"20"

ये वे तर्क हैं जो हमने दिए हैं।

वहाँ -- क्यों है?

आपने देखा होगा कि हम प्रोग्राम को इस प्रकार चला रहे हैं:

cargo run -- add 10 20

-- कार्गो के तर्कों को हमारे प्रोग्राम के तर्कों से अलग करता है। -- से पहले की हर चीज़ की व्याख्या कार्गो द्वारा की गई है। -- के बाद सब कुछ हमारे प्रोग्राम में भेज दिया जाता है।

तो:

cargo run -- add 10 20

मतलब:

Cargo
  |
  └── run the program
          |
          ├── "add"
          ├── "10"
          └── "20"

व्यक्तिगत तर्कों तक पहुँचना

हम एक सूचकांक का उपयोग करके एकत्रित तर्कों के अलग-अलग तत्वों तक पहुंच सकते हैं।

प्रोग्राम को इसमें बदलें:

fn main() {
    let args: Vec<String> = std::env::args().collect();

    println!("Operation: {}", args[1]);
    println!("First value: {}", args[2]);
    println!("Second value: {}", args[3]);
}

चलाएँ:

cargo run -- add 10 20

आपको मिलना चाहिए:

Operation: add
First value: 10
Second value: 20

हम args[0] के बजाय args[1] से क्यों शुरू करते हैं? क्योंकि पहला तर्क, args[0], प्रोग्राम का निष्पादन योग्य नाम या पथ है।

छवि 5

संख्याओं को पार्स करना

हम वह ऑपरेशन प्राप्त करने में सक्षम हैं जो उपयोगकर्ता करना चाहता है और मान, लेकिन समस्या यह है कि मान कमांड लाइन से स्ट्रिंग के रूप में आ रहे हैं, हमें किसी भी गणितीय ऑपरेशन को करने के लिए उन्हें संख्याओं में बदलने की आवश्यकता है। पार्स करने के लिए, हम कुछ इस तरह कर सकते हैं:

fn main() {
    let args: Vec<String> = std::env::args().collect();

    let a = args[2].parse::<f64>().unwrap();
    let b = args[3].parse::<f64>().unwrap();

    let result = a + b;

    println!("{result}");
}

इसे चलाएँ और आपको आउटपुट के रूप में 30 देखना चाहिए। अब हमें जो शेष काम करने की ज़रूरत है वह उन ऑपरेशनों को वापस लाना है जिनके बारे में हमने पहले टिप्पणी की थी और शर्तों का उपयोग करके यह चुनना है कि कौन सा ऑपरेशन करना है।

ऑपरेशन चुनना

अब तक, हम वह ऑपरेशन प्राप्त कर सकते हैं जिसे उपयोगकर्ता निष्पादित करना चाहता है और फ्लोटिंग-पॉइंट प्रकार में मान प्राप्त कर सकता है। हम रस्ट में कंडीशनल का उपयोग करने जा रहे हैं, मैं अभी स्थितियों में गहराई से नहीं जा रहा हूं, लेकिन हम इस प्रोजेक्ट में बुनियादी यदि-अन्यथा ब्लॉक का उपयोग करेंगे, तो चिंता करने की कोई आवश्यकता नहीं है।

fn main() {
    let args: Vec<String> = std::env::args().collect();

    let operation = &args[1];

    if operation == "add" {
        let a = args[2].parse::<f64>().unwrap();
        let b = args[3].parse::<f64>().unwrap();

        println!("{}", a + b);
    } else if operation == "subtract" {
        let a = args[2].parse::<f64>().unwrap();
        let b = args[3].parse::<f64>().unwrap();

        println!("{}", a - b);
    } else if operation == "multiply" {
        let a = args[2].parse::<f64>().unwrap();
        let b = args[3].parse::<f64>().unwrap();

        println!("{}", a * b);
    } else if operation == "divide" {
        let a = args[2].parse::<f64>().unwrap();
        let b = args[3].parse::<f64>().unwrap();

        println!("{}", a / b);
    } else if operation == "sqrt" {
        let value = args[2].parse::<f64>().unwrap();

        println!("{}", value.sqrt());
    } else if operation == "pow" {
	    let base = args[2].parse::<f64>().unwrap();
	    let exponent = args[3].parse::<i32>().unwrap();
	
	    println!("{}", base.powi(exponent));
    } else if operation == "sin" {
        let value = args[2].parse::<f64>().unwrap();

        println!("{}", value.sin());
    } else if operation == "cos" {
        let value = args[2].parse::<f64>().unwrap();

        println!("{}", value.cos());
    } else {
        println!("Unknown operation: {operation}");
    }
}

ठीक है, यह कोड की बहुत सारी पंक्तियाँ लगती हैं लेकिन हम केवल बहुत सारे if-else ब्लॉक का उपयोग कर रहे हैं, बस इतना ही। यह अन्य भाषाओं के समान ही है, इसलिए मैं कुछ भी स्पष्ट नहीं कर रहा हूँ। लेकिन संक्षेप में, हम वह ऑपरेशन प्राप्त कर रहे हैं जिसे उपयोगकर्ता निष्पादित करना चाहता है, फिर हम यह जांचने के लिए कि कौन सा ब्लॉक ऑपरेशन को संतुष्ट करता है, अगर-और ब्लॉक का एक समूह का उपयोग करते हैं और फिर उस ब्लॉक में, हम कमांड लाइन से मान पढ़ रहे हैं और ऑपरेशन कर रहे हैं और परिणाम प्रिंट कर रहे हैं।

हम प्रत्येक ब्लॉक के अंदर मान पढ़ रहे हैं ताकि हम आसानी से यह तय कर सकें कि किस ऑपरेशन के लिए कितने मानों की आवश्यकता है उदाहरण के लिए वर्गमूल के लिए केवल एक मान की आवश्यकता होती है लेकिन जोड़ने के लिए दो की आवश्यकता होती है

अब उनका परीक्षण करें:

cargo run -- sqrt 25
5
cargo run -- sin 0
0
cargo run -- cos 0
1

लीजिए, हमने अभी-अभी रस्ट में अपना मिनी वैज्ञानिक कैलकुलेटर बनाना पूरा किया है। यदि आप आगे प्रयोग करना चाहते हैं, तो स्वयं कुछ और ऑपरेशन जोड़ने का प्रयास करें। आप tan, log, या कुछ और जो आपको लगता है कि उपयोगी होगा, जोड़ सकते हैं।

और यदि आप अनुसरण कर रहे हैं और स्वयं कैलकुलेटर बना रहे हैं या कोई दिलचस्प संशोधन कर रहे हैं, तो बेझिझक मुझे एक्स पर टैग करें। मुझे यह देखना अच्छा लगेगा कि आप क्या बनाते हैं और श्रृंखला के साथ अनुसरण करने के लिए सराहना के एक छोटे संकेत के रूप में उनमें से अधिकांश को दोबारा पोस्ट करूंगा।

अगले लेख में, हम संख्या अनुमान लगाने का खेल बनाकर रस्ट में प्रवाह को नियंत्रित करने के बारे में सीखेंगे। अगले में मिलते हैं.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close