Cairo एक प्रोग्रामिंग लैंग्वेज है जिसे provable और verifiable computation के लिए डिज़ाइन किया गया है, विशेष रूप से Starknet जैसे zero-knowledge सिस्टम के संदर्भ में, जो Ethereum पर एक Layer 2 (L2) नेटवर्क है।
Cairo को विशेष रूप से प्रोग्राम एग्जीक्यूशन के STARK-आधारित proofs को सक्षम करने के लिए बनाया गया है। यह computations को off-chain कुशलतापूर्वक verify करने और फिर उन्हें succinct, trustless proofs के साथ on-chain साबित करने की अनुमति देता है।
हालांकि इस लैंग्वेज को ब्लॉकचेन यूज़ केस के लिए बनाया गया था, Cairo अपने डोमेन के भीतर लचीला (flexible) है, क्योंकि यह क्रिप्टोग्राफ़िक अखंडता (cryptographic integrity) के साथ off-chain verifiable computation का समर्थन करता है। Solidity के विपरीत, Cairo को स्मार्ट कॉन्ट्रैक्ट्स के संदर्भ के बाहर भी चलाया जा सकता है। इस अर्थ में, Cairo लगभग एक सामान्य-उद्देश्य (general-purpose) वाली लैंग्वेज की तरह व्यवहार करता है जो स्मार्ट कॉन्ट्रैक्ट्स और provable offchain computation तक सीमित है।
यह लेख इस बात का अवलोकन (overview) देता है कि लैंग्वेज कैसे काम करती है। हम प्रमुख डेटा प्रकारों (data types), नियंत्रण प्रवाह तंत्र (control flow mechanisms) और आमतौर पर उपयोग किए जाने वाले डेटा संरचनाओं (data structures) को कवर करेंगे।
Starknet में Cairo की भूमिका
Starknet जटिल computations के off-chain निष्पादन (execution) को सक्षम करने के लिए STARKs (Scalable Transparent Arguments of Knowledge) का उपयोग करता है, जबकि यह Ethereum की सुरक्षा और विकेंद्रीकरण (decentralization) को बनाए रखता है।
सभी Starknet स्मार्ट कॉन्ट्रैक्ट्स Cairo में लिखे गए हैं। ये कॉन्ट्रैक्ट्स Sierra नामक एक मध्यवर्ती प्रतिनिधित्व (intermediate representation) में compile होते हैं, जिसे फिर Casm (Cairo Assembly) में compile किया जाता है, जो एक low-level लैंग्वेज है जिसे CairoVM समझता है। CairoVM नियतात्मक रूप से (deterministically) Casm निर्देशों को निष्पादित करता है, एक execution trace तैयार करता है, और यह सुनिश्चित करता है कि प्रोग्राम STARK proof जनरेशन के लिए आवश्यक बाधाओं (constraints) का पालन करता है।
यह लेख Cairo प्रोग्रामिंग लैंग्वेज की मूल बातें पेश करता है और दिखाता है कि स्मार्ट कॉन्ट्रैक्ट्स के संदर्भ के बाहर इसे सामान्य-उद्देश्य (general-purpose) वाली लैंग्वेज के रूप में कैसे उपयोग किया जा सकता है। अगले भाग में जाने से पहले, विकास पर्यावरण (development environment) स्थापित करने के लिए नीचे दिए गए चरणों का पालन करें।
डेवलपमेंट एनवायरनमेंट सेट अप करना
-
एक खाली डायरेक्टरी बनाएं और उसमें नेविगेट करें।
डायरेक्टरी का कोई भी नाम हो सकता है, इस उदाहरण में, इसे
cairo_playgroundकहा गया है:mkdir cairo_playground && cd cairo_playground -
cairo_playgroundडायरेक्टरी के अंदर एक source फ़ोल्डर बनाएं:mkdir src -
srcफ़ोल्डर के अंदर, दो फ़ाइलें बनाएं:playground.cairo(नाम भिन्न हो सकता है) औरlib.cairo:touch src/playground.cairo && touch src/lib.cairo -
नई फ़ाइलों में निम्नलिखित सामग्री (content) जोड़ें।
playground.cairo:#[executable] fn main() { // Print message to terminal. println!("Hello from Rareskills!!!"); }lib.cairo:mod playground; -
प्रोजेक्ट रूट (
cairo_playground) में एकScarb.tomlफ़ाइल बनाएं:touch Scarb.tomlनिम्नलिखित सामग्री (content) जोड़ें:
[package] name = "cairo_playground" # HAS TO BE THE NAME OF THE ROOT DIRECTORY version = "0.1.0" edition = "2024_07" [cairo] enable-gas = false [dependencies] cairo_execute = "2.12.0" [[target.executable]] # A PATH TO THE FUNCTION WITH THE #[executable] ANNOTATION # <root-directory>::<file-name>::<function-name> function = "cairo_playground::playground::main"#[executable]एनोटेशन को बाद के उपखंड (subsection) में समझाया जाएगा।
सेटअप पूरा करने के बाद, डायरेक्टरी की संरचना कुछ इस तरह होनी चाहिए:

अंत में, Cairo प्रोग्राम (playgroung.cairo) का परीक्षण करने के लिए, निम्नलिखित कमांड चलाएँ:
scarb execute
Cairo लैंग्वेज के आवश्यक सिंटैक्स और डेटा प्रकार (data types)
Cairo का सिंटैक्स Rust से प्रेरित है, लेकिन इसे provable, verifiable कंप्यूटिंग के लिए अनुकूलित (optimized) किया गया है। इससे पहले कि हम डेटा प्रकारों और लॉजिक का पता लगाएं, Cairo में वैरिएबल और फ़ंक्शन डिक्लेरेशन जैसे निर्माण खंडों (building blocks) को समझना आवश्यक है।
वैरिएबल्स डिक्लेयर करना: let, mut और const
Cairo स्टैटिकली टाइप्ड है, और सभी वैरिएबल्स का प्रकार (type) कंपाइल समय पर डिक्लेयर किया जाना चाहिए। वैरिएबल डिक्लेरेशन के दौरान let कीवर्ड का उपयोग किया जाता है, जिसके बाद एक नाम, एक कोलन, एक प्रकार (type) और फिर वैल्यू आती है:
// let <NAME>: <dataType> = <value>;
let count: u8 = 42;
let name: felt252 = 'bob';
let active: bool = true;
वैरिएबल्स की म्यूटेबिलिटी (Variables mutability)
Cairo में वैरिएबल्स डिफ़ॉल्ट रूप से अपरिवर्तनीय (immutable) होते हैं। असाइनमेंट के बाद उन्हें संशोधित (modified) नहीं किया जा सकता है। परिवर्तन (mutation) को सक्षम करने के लिए, mut कीवर्ड का उपयोग किया जाता है, जैसा कि नीचे दिखाया गया है।
// let mut <NAME>: <Type> = <value>;
let mut total: u128 = 0;
total = total + 10;
एक कॉन्स्टेंट (constant) डिक्लेयर करना
Cairo में, const कीवर्ड का उपयोग उन निश्चित (fixed) वैल्यूज़ को परिभाषित करने के लिए किया जाता है जो कंपाइलेशन के दौरान ज्ञात होती हैं और रनटाइम पर बदली नहीं जा सकती हैं। कॉन्स्टेंट्स को प्रोग्राम सोर्स कोड में हार्डकोड किया जाता है, जिसका अर्थ है कि वे मेमोरी नहीं घेरते हैं, और उन्हें एक्सेस करने में शून्य रनटाइम लागत (zero runtime cost) लगती है।
यहाँ बताया गया है कि कॉन्स्टेंट कैसे डिक्लेयर किया जाता है:
// const <NAME>: <Type> = <value>;
const DECIMALS: u8 = 18;
Cairo में फ़ंक्शन्स डिक्लेयर करना
Cairo में फ़ंक्शन्स fn कीवर्ड का उपयोग करके डिक्लेयर किए जाते हैं। वे पैरामीटर पासिंग, रिटर्न वैल्यूज़ का समर्थन करते हैं और एक सख्त टाइपिंग सिस्टम का पालन करते हैं।
आइए नीचे दिए गए multiply फ़ंक्शन को देखें। फ़ंक्शन felt252 प्रकार के दो पैरामीटर (x, y) लेता है, और तीर (-> felt252 {..) के बाद एक felt252 वैल्यू भी लौटाता है जैसा कि नीचे दिखाया गया है:
// the function takes two parameters: `x` and `y`,
// both of type `felt252`, and returns a value of type `felt252`.
fn multiply(x: felt252, y: felt252) -> felt252 {
// The result of the multiplication expression is implicitly returned.
x * y
}
#[executable]
fn main() {
// Calls the multiply function with literal felt252 values: 3 and 4.
let result = multiply(3, 4); // result = 12
println!("This is the value of multiply(3, 4): {}", result);
}
किसी फ़ंक्शन में, हम return कीवर्ड का उपयोग करके स्पष्ट रूप से (explicitly) एक वैल्यू लौटा सकते हैं। हालाँकि, जैसा कि ऊपर दिए गए multiply फ़ंक्शन में देखा गया है, किसी वैल्यू को अंतर्निहित (implicitly) रूप से लौटाना भी संभव है। जब किसी फ़ंक्शन बॉडी में अंतिम एक्सप्रेशन के बाद सेमीकोलन (;) नहीं होता है, तो उसका परिणाम स्वचालित रूप से वापस आ जाता है।
#[executable] एट्रिब्यूट की व्याख्या
#[executable] एट्रिब्यूट किसी फ़ंक्शन को एक एंट्री पॉइंट के रूप में चिह्नित करता है जिसे सीधे Cairo रनर द्वारा कॉल किया जा सकता है। Cairo रनर वह प्रोग्राम है जो कंपाइल किए गए Cairo कोड को निष्पादित करने के लिए ज़िम्मेदार है, और शुरुआत बिंदु (starting point) के रूप में #[executable] से चिह्नित फ़ंक्शन्स की तलाश करता है।
इस एट्रिब्यूट के बिना, फ़ंक्शन एक टॉप-लेवल एंट्री पॉइंट के रूप में प्रदर्शित नहीं होता है और इसे अपने आप निष्पादित नहीं किया जा सकता है।
ऊपर दिए गए उदाहरण में, multiply फ़ंक्शन एक नियमित फ़ंक्शन है: इसे प्रोग्राम में अन्य फ़ंक्शन्स द्वारा कॉल किया जा सकता है लेकिन इसे अपने आप सीधे निष्पादित नहीं किया जा सकता है। इसके विपरीत, main फ़ंक्शन को #[executable] एट्रिब्यूट के साथ चिह्नित किया गया है, जो इसे एक एंट्री पॉइंट के रूप में नामित करता है जिसे सीधे Cairo रनर द्वारा चलाया जा सकता है।
ऊपर दिए गए main को निष्पादित करने के लिए, अपने टर्मिनल में scarb execute कमांड दर्ज करें। आउटपुट नीचे दिखाया गया है:

नोट: #[executable] एट्रिब्यूट smart contracts पर लागू नहीं होता है।
वास्तव में, ऊपर दिया गया उदाहरण एक स्मार्ट कॉन्ट्रैक्ट नहीं है, बल्कि एक नियमित Cairo प्रोग्राम है। यह इसलिए संभव है क्योंकि, Solidity के विपरीत जो डोमेन-विशिष्ट (domain-specific) है, Cairo को स्मार्ट कॉन्ट्रैक्ट के संदर्भ के बाहर भी निष्पादित किया जा सकता है। Cairo रनर का उपयोग करके, आप Starknet पर उन्हें डिप्लॉय किए बिना स्टैंडअलोन प्रोग्राम लिख और चला सकते हैं।
Cairo फ़ंक्शन्स में डेटा प्रिंट करना
अब जब आपने देख लिया है कि स्टैंडअलोन Cairo प्रोग्राम कैसे चलाया जाता है, तो आइए जानें कि Cairo आपको टर्मिनल पर वैल्यूज़ कैसे प्रिंट करने देता है।
लैंग्वेज मानक डेटा प्रकारों (standard data types) को प्रिंट करने के लिए दो macros प्रदान करती है:
println!(जो आउटपुट के बाद एक नई लाइन प्रिंट करता है)print!(जो बिना किसी नई लाइन के इनलाइन आउटपुट प्रिंट करता है)।
दोनों मैक्रोज़ कम से कम एक पैरामीटर लेते हैं: एक ByteArray स्ट्रिंग, जिसमें शून्य या अधिक प्लेसहोल्डर (जैसे, {}, {var}) हो सकते हैं, जिसके बाद एक या अधिक तर्क (arguments) होते हैं जिन्हें उन प्लेसहोल्डर्स में क्रमानुसार या नाम के आधार पर प्रतिस्थापित (substituted) किया जाता है।
यह समझने के लिए नीचे दिया गया कोड देखें कि print! या println! को कैसे फ़ॉर्मेट किया जा सकता है:
#[executable]
fn main() {
let version = 2;
let released = 2023;
//contains one parameter: a ByteArray string
println!("Welcome to the Cairo programming language!");
// Positional formatting
println!("Version: {}, Released in: {}", version, released);
// Mixing named and positional placeholders
println!("Cairo v{} was released in {released}", version);
}
Cairo में डेटा प्रकार (Data Types)
अब जब हमने देख लिया है कि Cairo में वैरिएबल्स कैसे डिक्लेयर किए जाते हैं, तो आइए Cairo के मुख्य डेटा प्रकारों का पता लगाएं।
1. felt252: मुख्य न्यूमेरिक प्रकार (The Core Numeric Type)
Cairo में, सबसे मौलिक (fundamental) डेटा प्रकार एक फ़ील्ड एलिमेंट है जिसे felt252 द्वारा दर्शाया जाता है। यह लैंग्वेज में डिफ़ॉल्ट न्यूमेरिक प्रकार है, और Cairo VM द्वारा उपयोग किए जाने वाले प्राइम फ़ील्ड (prime field) के एक एलिमेंट को दर्शाता है। यह फ़ील्ड नीचे दिखाया गया है:
p = 2^{251} + 17*2^{192} + 1
इसका मतलब है कि एक felt252 वैल्यू 0 से लेकर p - 1 तक हो सकती है। felt252 वैल्यूज़ पर किया जाने वाला सभी अंकगणित (arithmetic) इस फ़ील्ड पर मॉड्यूलर अंकगणित (modular arithmetic) होता है। जब कोई परिणाम p−1 से अधिक हो जाता है, तो यह वापस 0 पर रैप (wrap) हो जाता है, ठीक उसी तरह जैसे घड़ी में घंटे रैप होते हैं।
नीचे दिया गया कोड दिखाता है कि कैसे felt252 अधिकतम वैल्यू (p - 1) से अधिक के अंकगणितीय संचालन शून्य पर रैप होते हैं।
// The actual maximum value for felt252 in Cairo (p - 1 where p is the prime modulus)
const MAX_FELT252: felt252 = 3618502788666131213697322783095070105623107215331596699973092056135872020480;
#[executable]
fn main() {
let mut anyvalue = -5;
let result = MAX_FELT252 + anyvalue;
// When adding -5 to MAX_FELT252, we get MAX_FELT252 - 5 (still less than p)
if result != 0 {
println!("Result is less than p: {}", result);
println!("This means MAX_FELT252 - {} did not wrap to 0", 5);
}
// Now let's try adding a positive value that will cause wrapping
anyvalue = 1; // Reset to 1 to test wrapping
let wrap_result = MAX_FELT252 + anyvalue;
if wrap_result == 0 {
println!("Confirmed: MAX_FELT252 + {} wraps to 0", anyvalue);
} else {
println!("Unexpected: MAX_FELT252 + {} = {}", anyvalue, wrap_result);
}
// Test with a larger positive value
anyvalue = 10;
let wrap_result_10 = MAX_FELT252 + anyvalue;
println!("MAX_FELT252 + {} = {}", anyvalue, wrap_result_10);
}
टर्मिनल आउटपुट:

इस रैपिंग व्यवहार (wrapping behavior) के कारण, यदि सावधानी से न संभाला जाए, तो अनपेक्षित रैपिंग के कारण अंकगणितीय त्रुटियाँ (अर्थात, ओवरफ़्लो) हो सकती हैं।
इसे दूर करने के लिए, Cairo निश्चित-चौड़ाई (fixed-width) वाले इंटीजर प्रकार u8..u256 और हस्ताक्षरित (signed) इंटीजर i8..i256 भी प्रदान करता है, जो रनटाइम पर ओवरफ़्लो/अंडरफ़्लो की जांच करते हैं। यदि कोई ऑपरेशन वैध सीमा (valid range) को पार करने का प्रयास करता है, तो प्रोग्राम पैनिक (panic) करेगा (अर्थात, त्रुटि के साथ रुक जाएगा)।
जहाँ अत्यधिक अनुकूलन (extreme optimizations) की आवश्यकता हो, वहाँ felt252 का उपयोग करें, क्योंकि अन्य सभी प्रकार अंततः हुड के नीचे (under the hood) felt252 के रूप में दर्शाए जाते हैं। सामान्य अंकगणित और सुरक्षा के लिए, इंटीजर प्रकारों का उपयोग करने की अनुशंसा की जाती है, क्योंकि वे बिल्ट-इन ओवरफ़्लो सुरक्षा प्रदान करते हैं।
felt252 में विभाजन (Division)
Cairo के felt252 प्रकार में फ़ील्ड एलिमेंट्स परिमित फ़ील्ड अंकगणितीय सिद्धांतों (finite field arithmetic principles) के तहत काम करते हैं, जिसका अर्थ है कि वे शेषफल (remainder) या निश्चित-चौड़ाई (fixed-width) वाले इंटीजर्स की तरह पारंपरिक इंटीजर विभाजन का समर्थन नहीं करते हैं। इसके बजाय, विभाजन a / b का मूल्यांकन a × b^(-1) mod P के रूप में होता है, जहाँ b एक गैर-शून्य वैल्यू है।
b⁻¹ को b मॉड्यूलो P का मॉड्यूलर गुणक प्रतिलोम (modular multiplicative inverse) कहा जाता है।
यदि a = 1, और b=2, तो हमारे पास 1 × 2⁻¹ होगा।
चूंकि,
नीचे दिए गए कोड ब्लॉक में, हम दिखाएंगे कि उपरोक्त प्रमाण कैसे सत्य है और बिना शेषफल (remainder) या शेषफल के साथ felt252 विभाजन का व्यवहार देखेंगे।
use core::felt252_div;
#[executable]
fn main() {
// (p + 1) / 2
let P_plus_1_halved = 1809251394333065606848661391547535052811553607665798349986546028067936010241;
assert!(felt252_div(1, 2) == P_plus_1_halved);
println!("this is the value of felt252_div(1, 2): {}", felt252_div(1, 2));
//divisions with zero remainder
assert!(felt252_div(2, 1) == 2);
println!("this is the value of felt252_div(2, 1): {}", felt252_div(2, 1));
assert!(felt252_div(15, 5) == 3);
println!("this is the value of felt252_div(15, 5): {}", felt252_div(15, 5));
//division with remainder
println!("this is the value of felt252_div(7, 3): {}", felt252_div(7, 3));
println!("this is the value of felt252_div(4, 3): {}", felt252_div(4, 3));
}
टर्मिनल आउटपुट:

जैसा कि ऊपर दिए गए परीक्षण में देखा गया है, Cairo फ़ील्ड में विभाजन इंटीजर विभाजन के समान ही काम करता है जब कोई शेषफल नहीं होता है।
हालाँकि, यह तब अलग होता है जब विभाजनों में शेषफल (remainder) होते हैं। उदाहरण के लिए, यदि हम 4 को 3 से विभाजित करते हैं, तो हम यह नहीं पूछ रहे हैं कि “चार में तीन कितनी बार जाता है,” बल्कि यह पूछ रहे हैं कि “इस फ़ील्ड में किस वैल्यू को तीन से गुणा करने पर चार मिलता है?”
फ़ील्ड अंकगणित में, उत्तर चार और तीन के मॉड्यूलर इनवर्स का गुणनफल (product) है। यह सुनिश्चित करता है कि परिणाम को जब तीन से गुणा किया जाता है, तो फ़ील्ड के प्राइम का चार मॉड्यूलो प्राप्त होता है।
क्या होता है जब वैरिएबल्स को बिना प्रकार (type) के डिक्लेयर किया जाता है?
Cairo में, जब आप किसी प्रकार को निर्दिष्ट किए बिना एक न्यूमेरिक लिटरल (numeric literal) असाइन करते हैं, जैसा कि नीचे दिखाया गया है, तो कंपाइलर स्वचालित रूप से मान लेता है कि वैल्यू felt252 प्रकार की है।
let count = 42;
// count's is of type felt252
ऐसा इसलिए है क्योंकि felt252 Cairo का डिफ़ॉल्ट न्यूमेरिक प्रकार है, ठीक उसी तरह जैसे कुछ अन्य लैंग्वेज में डिफ़ॉल्ट रूप से int का उपयोग किया जाता है।
2. अनसाइंड (Unsigned) इंटीजर्स: u8..u256
Cairo में, u8, u16, u32, u64, और u128 जैसे निश्चित-चौड़ाई (fixed-width) वाले इंटीजर्स बड़े felt252 प्रकार के उपसमुच्चय (subsets) हैं, जिसका अर्थ है कि उनकी वैल्यूज़ पूरी तरह से felt252 में फिट हो सकती हैं; उन्हें सुरक्षित रूप से फ़ील्ड एलिमेंट्स के रूप में दर्शाया जा सकता है क्योंकि उनकी अधिकतम वैल्यूज़ felt252 के अधिकतम मान से कम होती हैं।
Table 1: अनसाइंड इंटीजर्स की रेंज
| प्रकार | आकार (bits) | रेंज |
|---|---|---|
u8 |
8-bit | 0 to 255 |
u64 |
64-bit | 0 to 2⁶⁴ - 1 |
u128 |
128-bit | 0 to 2¹²⁸ - 1 |
u256 |
256-bit | 0 to 2²⁵⁶ - 1 (composite) |
u256 , जैसा कि तालिका 1 में देखा गया है, felt252 के अधिकतम मान से अधिक है और इसलिए, एक ही फ़ील्ड एलिमेंट में फिट नहीं होता है। हुड के नीचे (under the hood), Cairo u256 को दो u128 वैल्यूज़ से बने स्ट्रक्चर (struct) के रूप में दर्शाता है:
struct u256 {
low: u128, // Least significant 128 bits
high: u128, // Most significant 128 bits
}
उदाहरण के लिए, u256 प्रकार की वैल्यू 7 को इस प्रकार आधा किया जाता है:
let value: u256 = 7;
// __________________________256-bit_____________________________
// | |
// 0x0000000000000000000000000000000000000000000000000000000000000007
// ________high 128-bit__________ __________low 128-bit_________
// | | | |
// 0x00000000000000000000000000000000 00000000000000000000000000000007
3. साइंड (Signed) इंटीजर्स: i8, i16, i32, i64, i128
Cairo में साइंड (Signed) इंटीजर्स को छोटे अक्षर i का उपयोग करके लिखा जाता है जिसके बाद बिट की चौड़ाई होती है, जैसे i8, i16, i32, i64, या i128। प्रत्येक साइंड प्रकार शून्य के आसपास केंद्रित एक सीमा (range) के भीतर वैल्यूज़ का प्रतिनिधित्व कर सकता है, जिसकी गणना सूत्र का उपयोग करके की जाती है:
से
उदाहरण के लिए, i8 की रेंज -128..127 है।
Cairo में साइंड और अनसाइंड इंटीजर्स में ओवरफ़्लो/अंडरफ़्लो व्यवहार
नीचे दिए गए कोड में, हमने इंटीजर्स (साइंड और अनसाइंड) के व्यवहार का परीक्षण करने के लिए u256 (एक संदर्भ के रूप में) का उपयोग किया, जब वे ओवरफ़्लो/अंडरफ़्लो का सामना करते हैं।
// Maximum value for u256: 2^256 - 1
const MAX_U256: u256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
fn add_u256(a: u256, b: u256) -> u256 {
a + b
}
fn sub_u256(a: u256, b: u256) -> u256 {
a - b
}
fn multiply_u256(a: u256, b: u256) -> u256 {
a * b
}
#[executable]
fn main() {
println!("Testing u256 panic behavior");
println!("MAX_U256: {}", MAX_U256);
// Note: calls that panic will terminate the entire program immediately
//(comment out all other panic calls to see each result individually)
let result = sub_u256(MAX_U256, 1);
println!("result(less than MAX_U256): {}", result);
// This will panic on underflow
let result = sub_u256(0, 1);
println!("Underflow result: {}", result);
//returns -> error: Panicked with 0x753235365f616464204f766572666c6f77 ('u256_add Overflow').
// This will panic on overflow
let result = add_u256(MAX_U256, 1);
println!("Overflow result: {}", result);
//returns -> error: Panicked with 0x753235365f616464204f766572666c6f77 ('u256_add Overflow').
// This will also panic on overflow
let mult_result = multiply_u256(MAX_U256, 2); //
println!("Mult result: {}", mult_result);
//returns -> error: Panicked with 0x753235365f6d756c204f766572666c6f77 ('u256_mul Overflow').
}
जैसा कि ऊपर दिखाया गया है, सभी अंकगणितीय संचालन जो u256 की अधिकतम वैल्यू से अधिक होते हैं, उनका परिणाम पैनिक एरर (panic error) होता है।
4. bool: true या false
एक bool का उपयोग लॉजिकल वैल्यूज़ को दर्शाने के लिए किया जाता है: true या false। आंतरिक रूप से, एक bool को felt252 के रूप में एन्कोड किया जाता है जिसकी वैल्यू 0 (false) या 1 (true) होती है।
Cairo कंपाउंड प्रकार (Compound Types)
कंपाउंड प्रकार कई वैल्यूज़ को एक साथ समूहित (group) करते हैं, जिससे Cairo में संरचित (structured) और अभिव्यंजक डेटा प्रतिनिधित्व (expressive data representation) की अनुमति मिलती है।
टुपल्स (Tuples)
टुपल्स (Tuples) विभिन्न प्रकारों के वैल्यूज़ के निश्चित सेट (fixed sets) रखते हैं। वे फ़ंक्शन्स से कई वैल्यूज़ लौटाने या संबंधित डेटा को अस्थायी रूप से समूहित (grouping) करने के लिए उपयोगी हैं।
let pair: (felt252, bool) = (42, true);
// Accessing tuple elements by destructuring
let (first_element, first_element) = pair;
स्ट्रक्ट्स (Structs)
स्ट्रक्ट्स (Structs) नामित फ़ील्ड्स (named fields) के साथ कस्टम डेटा प्रकार हैं।
// Define the struct
struct Point {
x: felt252,
y: felt252,
}
#[executable]
fn main() {
let p = Point { x: 3, y: 4 };
// Accessing struct fields
let x_coordinate = p.x;
let y_coordinate = p.y;
println!("The x coordinate of point p is: {}", x_coordinate);
println!("The y coordinate of point p is: {}", y_coordinate);
}
एनम्स (Enums)
Enums कई नामित वेरिएंट (named variants) वाले प्रकार हैं, जहाँ प्रत्येक वेरिएंट वैकल्पिक रूप से डेटा रख सकता है। वे उन वैल्यूज़ को दर्शाने के लिए एकदम सही हैं जो कई अलग-अलग प्रकारों में से एक हो सकते हैं।
enum Direction {
North,
South,
East,
West,
}
// Enum with associated data
enum Message {
Quit,
Move: Point,
Write: felt252,
Color: (felt252, felt252, felt252),
}
// Using enums with pattern matching
let msg = Message::Move(Point { x: 10, y: 20 });
match msg {
Message::Quit => { /* handle quit */ },
Message::Move(point) => { /* handle move with point data */ },
Message::Write(text) => { /* handle write with text */ },
Message::Color((r, g, b)) => { /* handle color with RGB values */ },
}
स्ट्रिंग्स (Strings), शॉर्ट स्ट्रिंग्स, बाइटएरे (bytearray)
उच्च-स्तरीय (high-level) लैंग्वेज की तुलना में Cairo में टेक्स्ट हैंडलिंग निचले स्तर (lower-level) की है। इस लैंग्वेज में Rust या JavaScript की तरह कोई पारंपरिक String प्रकार नहीं है, लेकिन यह टेक्स्टुअल डेटा को संभालने के लिए दो मुख्य प्राइमिटिव (primitives) प्रदान करता है:
- शॉर्ट स्ट्रिंग्स (Short strings):
felt252(याbytes31) में एन्कोड किए गए स्ट्रिंग लिटरल्स, जो 31 बाइट्स तक सीमित हैं। ByteArray: गतिशील रूप से आकार (dynamically-sized) वाले ASCII वर्णों और बाइट अनुक्रमों (byte sequences) के लिए एक बिल्ट-इन प्रकार।
आइए इन स्ट्रिंग प्रकारों के विवरण के बारे में जानें।
शॉर्ट स्ट्रिंग्स (Short Strings): felt252 में कॉम्पैक्ट ASCII
जब आपका स्ट्रिंग प्रतिनिधित्व (representation) छोटा होता है या 31 ASCII वर्णों से अधिक नहीं होता है, तो आप इसे शॉर्ट स्ट्रिंग्स के रूप में दर्शा सकते हैं। Cairo में शॉर्ट स्ट्रिंग्स को सीधे एक ही felt252 में पैक किया जाता है, जिसमें प्रत्येक वर्ण को उसके ASCII मान (1 बाइट = 8 बिट्स) का उपयोग करके एन्कोड किया जाता है। चूंकि एक felt252 252 बिट्स रखता है, आप एक ही फ़ील्ड एलिमेंट में 31 ASCII वर्णों तक स्टोर कर सकते हैं।
आइए लोअरकेस 'hello world' लेते हैं, कुल 11 वर्ण, जो 31-वर्ण सीमा के भीतर है।
// Note the single quotes around the string.
let greeting: felt252 = 'hello world'; // Fits within 31 ASCII characters
// OR
let greeting: bytes31 = 'hello world'.try_into().unwrap(); // Fits within 31 ASCII characters
// ' h e l l o w o r l d '
// → ASCII bytes: 68 65 6C 6C 6F 20 77 6F 72 6C 64
// → Hex: 0x68656c6c6f20776f726c64
यदि हम 'hello world' उदाहरण के प्रत्येक वर्ण को उसके ASCII code में मैप करते हैं, और उन बाइट्स को एक ही हेक्स (hex) वैल्यू में पैक करते हैं, तो बाएं से दाएं हमारे पास होगा: 0x68656c6c6f20776f726c64।
बाइट एरे स्ट्रिंग्स (Byte Arrays strings)
Cairo में ByteArray प्रकार ASCII वर्णों और मनमाने (arbitrary) बाइट अनुक्रमों को संभालने के लिए डिज़ाइन किया गया है जो एकल felt252 की 31-बाइट सीमा से अधिक हैं। यह इसे गतिशील-लंबाई (dynamic-length) वाले डेटा के प्रबंधन के लिए आवश्यक बनाता है।
// Note the double quotes around the long string.
let long_string: ByteArray = "Hello, Cairo! This is a longer string that exceeds 31 bytes and demonstrates ByteArray usage perfectly.";
आंतरिक रूप से, ByteArray एक हाइब्रिड स्टोरेज संरचना का उपयोग करता है। नीचे दिया गया कोड ब्लॉक दिखाता है कि कैसे ByteArray स्ट्रक्ट में तीन फ़ील्ड शामिल हैं जो बाइट डेटा को स्टोर करने के लिए एक साथ काम करते हैं:
pub struct ByteArray {
pub(crate) data: Array<bytes31>, // Full 31-byte chunks
pub(crate) pending_word: felt252, // Incomplete bytes (up to 30 bytes)
pub(crate) pending_word_len: usize, // Number of bytes in pending_word
}
dataफ़ील्डbytes31के रूप में संग्रहीत पूर्ण 31-बाइट चंक्स (chunks) रखता है।pending_wordफ़ील्ड बचे हुए बाइट्स को रखता है जो एक पूर्ण चंक नहीं बनाते हैं।pending_word_lenpending_wordमें संग्रहीत बाइट्स की सटीक संख्या को ट्रैक करता है।
pending_word अधिकतम 30 बाइट्स स्टोर कर सकता है, 31 नहीं। यदि आपके पास ठीक 31 बाइट्स उपलब्ध हैं, तो उन्हें data में एक पूर्ण चंक के रूप में संग्रहीत किया जाता है। कुल मिलाकर 31 बाइट्स से कम वाले बाइट एरेज़ के लिए, data खाली रहता है और सभी सामग्री pending_word में रहती है।
अब, लंबाई के आधार पर डेटा कैसे संग्रहीत किया जाता है यह देखने के लिए कुछ ByteArray उदाहरण बनाएं:
#[executable]
fn main() {
// Short string (≤30 bytes) - stored entirely in pending_word
let short_data: ByteArray = "Hello Cairo developers!"; // 23 bytes in pending_word
// Medium string (31-60 bytes) - one chunk in data + remainder in pending_word
let medium_data: ByteArray = "This is a longer string that demonstrates ByteArray storage"; // 58 bytes total
// Long string (>62 bytes) - multiple chunks in data + remainder in pending_word
let long_data: ByteArray = "ByteArray stores data efficiently using 31-byte chunks in the data field, with any remaining bytes stored in pending_word field"; // 127 bytes total
}
Cairo में कंट्रोल फ्लो (Control flow)
Cairo मानक कंट्रोल फ्लो संरचनाओं (constructs) जैसे कि कंडीशनल स्टेटमेंट्स और लूप्स का समर्थन करता है, जो डेवलपर्स को ब्रांचिंग प्रोग्राम लिखने की अनुमति देते हैं।
if, else if, else
Cairo ब्रांचिंग लॉजिक के लिए if, else if, और else ब्लॉक्स का उपयोग करता है, ठीक वैसे ही जैसे Rust या अन्य मुख्यधारा (mainstream) लैंग्वेज में होता है।
यहाँ एक उदाहरण दिया गया है जो दिखाता है कि Cairo में if स्टेटमेंट कैसे लिखे जाते हैं।
use core::felt252_div;
#[executable]
fn main() {
let x: u32 = 5; // Explicitly type as u32
let wrecked_pie = felt252_div(22, 7);
let _result = if x > 10 {
wrecked_pie - 1000
} else if x == 10 {
0
} else {
wrecked_pie
};
println!("this is the value of result: {}", _result);
}
ध्यान दें कि यदि हमने ऊपर दिए गए उदाहरण में x को felt252 के रूप में परिभाषित किया होता, तो प्रोग्राम कंपाइल समय पर विफल हो जाता। ऐसा इसलिए है क्योंकि felt252 PartialOrd trait को लागू नहीं करता है, जो <, >, <=, और >= जैसे तुलना ऑपरेटरों (comparison operators) का उपयोग करने के लिए आवश्यक है। यह सीमा Cairo में एक जानबूझकर किया गया डिज़ाइन विकल्प है ताकि उन क्रिप्टोग्राफ़िक गलतियों को रोका जा सके जो फ़ील्ड एलिमेंट्स के संख्यात्मक क्रम (numerical ordering) पर भरोसा करने से उत्पन्न हो सकती हैं।
लूप्स (Loops) (loop, while, और for)
Cairo लूप्स के तीन प्राथमिक रूपों का समर्थन करता है: loop, while, और for, जिनमें से प्रत्येक के विशिष्ट उपयोग के मामले और बाधाएं (constraints) हैं।
loops: loop कीवर्ड एक अनंत लूप (infinite loop) बनाता है, जो अन्य भाषाओं में while true के समान है। यह अनिश्चित काल तक चलता है जब तक कि break स्टेटमेंट के साथ स्पष्ट रूप से बाहर (exit) न निकल जाए। यह संरचना तब उपयोगी होती है जब पुनरावृत्तियों (iterations) की संख्या पहले से ज्ञात नहीं होती है, और आप लूप को समाप्त करने के लिए आंतरिक स्थितियों पर निर्भर करते हैं।
यहाँ एक शर्त पूरी होने तक संख्याओं को जोड़ने के लिए loop का उपयोग करने का एक उदाहरण दिया गया है:
fn loop_sum(limit: felt252) -> felt252 {
let mut i = 0;
let mut sum = 0;
loop {
if i == limit {
break;
}
sum += i;
i += 1;
}
sum
}
इस उदाहरण में, loop अनिश्चित काल तक जारी रहता है जब तक कि i == limit न हो जाए, जिस बिंदु पर break लूप से बाहर निकल जाता है।
while: Cairo में while लूप तब तक निष्पादित होता है जब तक दी गई शर्त (condition) सत्य (true) का मूल्यांकन करती है। यह सशर्त पुनरावृत्ति (conditional iteration) के लिए सबसे उपयुक्त है जब अंत की स्थिति का रनटाइम पर मूल्यांकन किया जाता है। लूप की स्थिति नियतात्मक (deterministic) होनी चाहिए और निष्पादन के दौरान ज्ञात वैल्यूज़ पर आधारित होनी चाहिए।
let mut i = 0;
while i < 5 {
// Do something
i += 1;
}
for: Cairo में for लूप केवल स्थिर रूप से परिभाषित (statically defined) सीमाओं (ranges) के साथ काम करता है। इसका मतलब है कि आप for i in 0..n सिंटैक्स का उपयोग करके एक कॉन्स्टेंट या लिटरल सीमा पर पुनरावृत्ति (iterate) कर सकते हैं, जहाँ n एक कंपाइल-टाइम कॉन्स्टेंट या लूप की शुरुआत में एक ज्ञात वैल्यू होना चाहिए।
नीचे दिए गए इस उदाहरण में, हमने for कीवर्ड का उपयोग करके एक एरे (array) (जिसे हम शीघ्र ही समझाएंगे) पर लूप किया।
use core::array::ArrayTrait;
#[executable]
fn main() {
let mut a = ArrayTrait::new();
a.append(10);
a.append(20);
a.append(30);
a.append(40);
a.append(50);
let len = a.len();
for i in 0..len {
let val = a.at(i);
// You can use `val` here however you need
let _ = val;
}
}
Cairo में एरेज़ (Arrays) और डिक्शनरीज़ (Dictionaries)
Cairo में Arrays एक ही प्रकार की वैल्यूज़ के क्रमित संग्रह (ordered collections) हैं। Cairo के अपरिवर्तनीय मेमोरी मॉडल (immutable memory model) के कारण, एक बार जोड़े जाने के बाद मौजूदा एलिमेंट्स को संशोधित नहीं किया जा सकता है। एलिमेंट्स को append() का उपयोग करके अंत में जोड़ा जा सकता है और pop_front() का उपयोग करके सामने से हटाया जा सकता है, जो एक Option<T> लौटाता है और तार्किक प्रारंभिक स्थिति (logical start position) को आगे बढ़ाता है। यह कतार-जैसा (queue-like) व्यवहार FIFO (फर्स्ट-इन, फर्स्ट-आउट) संचालन की अनुमति देता है।
एरेज़ को Array<T> प्रकार का उपयोग करके लागू किया जाता है, जिसमें विधियाँ (methods) array::ArrayTrait द्वारा प्रदान की जाती हैं। इसलिए, नए एरेज़ ArrayTrait::new() कॉल का उपयोग करके बनाए जाते हैं।
नीचे दिया गया कोड दिखाता है कि एक नया एरे कैसे बनाया जाता है।
use array::ArrayTrait;
let mut numbers = ArrayTrait::<felt252>::new();
स्थानीय (मेमोरी) एरेज़ डिफ़ॉल्ट रूप से अपरिवर्तनीय (immutable) होते हैं। इसलिए हम उन्हें परिवर्तनीय (mutable) बनाने के लिए let mut का उपयोग करते हैं, जैसा कि नीचे दिखाया गया है।
इसके बाद, हम .append(value) को कॉल करके एरे में आइटम जोड़ सकते हैं।
numbers.append(10); // the element 10 is appended to index 0
numbers.append(20); // the element 10 is appended to index 1
वैकल्पिक रूप से, हम संकलन-समय (compile-time) पर क्रमिक रूप से आइटम जोड़ने के लिए array! का उपयोग कर सकते हैं:
let arr = array![1, 2, 3, 4, 5];
एरे मेथड (Array Method)
प्रत्येक एरे बिल्ट-इन मेथड्स द्वारा समर्थित होता है जो array::ArrayTrait के माध्यम से उजागर होते हैं। यहाँ Cairo की एरे मेथड्स दी गई हैं:
.new(): एक खाली एरे बनाता है।.append(value): एरे के अंत में एक आइटम जोड़ता है।.pop_front(): एरे के सामने से एलिमेंट्स को हटाता है।.len(): एलिमेंट्स की संख्या लौटाता है।.pop_front(): अंतिम एलिमेंट को हटाता है और लौटाता है।isEmpty(): यदि एरे खाली है तोtrueलौटाता है, अन्यथाfalseलौटाता है।.get(index)याat(index): एक विशिष्ट इंडेक्स पर एक आइटम पढ़ता है।
Cairo में, एरे में एलिमेंट्स तक पहुँचने के लिए .get(index) और .at(index) दोनों का उपयोग किया जाता है, लेकिन उनके व्यवहार में अंतर है। .get(index) मेथड एक Option<T> लौटाती है, जिसका अर्थ है कि परिणाम या तो Option::Some(value) हो सकता है यदि इंडेक्स सीमा के भीतर है, या Option::None यदि यह नहीं है। यह .get() को अधिक सुरक्षित विकल्प बनाता है, विशेष रूप से उन स्थितियों में जहाँ आप यह गारंटी नहीं दे सकते कि इंडेक्स वैध (valid) है।
दूसरी ओर, .at(index) आपको बिना Option में लपेटे सीधे वैल्यू देता है। हालांकि यह एक्सेस को सरल बनाता है जब इंडेक्स के वैध होने का पता होता है, यह एक महत्वपूर्ण ट्रेडऑफ़ के साथ आता है: यदि इंडेक्स सीमा से बाहर (out of bounds) है, तो प्रोग्राम पैनिक करेगा और क्रैश हो जाएगा।
Cairo में कई डेटा प्रकारों (multiple data types) के एरेज़
आप सीधे एक ही एरे में कई अलग-अलग डेटा प्रकारों को स्टोर नहीं कर सकते हैं, क्योंकि एरेज़ सजातीय (homogeneous) होते हैं (सभी एलिमेंट्स का एक ही प्रकार का होना आवश्यक है)।
हालाँकि, आप विभिन्न प्रकारों को एक एकल एकीकृत (unified) प्रकार में लपेटने (wrap) के लिए एक कस्टम एनम (custom enum) या स्ट्रक्ट (struct) का उपयोग करके इस सीमा के आसपास काम कर सकते हैं।
नीचे दिया गया उदाहरण दिखाता है कि एनम का उपयोग करके इसके आसपास कैसे काम किया जाए।
use core::array::ArrayTrait;
//NOTE:
// The Drop trait allows automatic cleanup when this type goes out of scope.
// Basic types like felt252, u8, bool, etc. have automatic Drop implementations, But
// Custom types like enums and structs typically need to derive Drop explicitly.
// Felt252Dict or other non-droppable types cannot implement Drop.
#[derive(Drop)]
enum MixedValue {
Felt: felt252,
SmallNumber: u8,
Flag: bool,
FeltArray: Array<felt252>,
}
#[executable]
fn main() {
let mut mixed: Array<MixedValue> = ArrayTrait::new();
mixed.append(MixedValue::Felt(2025));
mixed.append(MixedValue::SmallNumber(7_u8));
mixed.append(MixedValue::Flag(true));
let mut nested_array: Array<felt252> = ArrayTrait::new();
nested_array.append(1);
nested_array.append(2);
nested_array.append(3);
mixed.append(MixedValue::FeltArray(nested_array));
}
डिक्शनरीज़ (Dictionaries) (Felt252Dict<T> डेटा प्रकार)
Solidity में मैपिंग के समान, Felt252Dict<T> एक डिक्शनरी जैसा डेटा प्रकार है जो कुंजी-मूल्य जोड़े (key-value pairs) के संग्रह को दर्शाता है जहाँ प्रत्येक कुंजी अद्वितीय होती है और एक संबंधित वैल्यू T से जुड़ी होती है। इसकी कार्यक्षमता या मेथड्स को कोर लाइब्रेरी में Felt252DictTrait ट्रेट (trait) में लागू किया गया है।
कुंजी (key) का प्रकार felt252 तक सीमित है, जबकि उनके वैल्यू डेटा प्रकार को निर्दिष्ट किया गया है। आंतरिक रूप से, Felt252Dict<T> प्रविष्टियों (entries) की एक सूची के रूप में कार्य करता है जिसमें प्रत्येक कुंजी से जुड़ी वैल्यू शून्य पर इनिशियलाइज़ होती है। एक बार एक नई प्रविष्टि सेट हो जाने पर, शून्य वैल्यू पिछली प्रविष्टि के रूप में सेट हो जाती है। इसलिए, यदि कोई गैर-मौजूद (non-existent) कुंजी दर्ज की जाती है, तो किसी एरर या अपरिभाषित (undefined) वैल्यू के बजाय 0 वापस करने के लिए Felt252DictTrait के तहत zero_default मेथड को कॉल किया जाएगा। हालाँकि, यह ट्रेट जटिल प्रकारों के लिए उपलब्ध नहीं है (कारण अगले उपखंड में है)।
यहाँ एक सरल उदाहरण दिया गया है कि Felt252Dict<T> कुंजी-मूल्य (key-value) जोड़े के साथ कैसे काम किया जाए।
use core::dict::Felt252Dict;
#[executable]
fn main() {
// Create the dictionary
let mut balances: Felt252Dict<u64> = Default::default();
// Insert only 'clark'
balances.insert('clark', 50);
// Get balance for 'clark'
let clark_balance = balances.get('clark');
println!("This is clark_balance: {}", clark_balance);
assert!(clark_balance == 100, "clark_balance is not 100");
// Try to get 'jane' — not inserted, returns 0
let jane_balance = balances.get('jane');
println!("This is jane_balance: {}", jane_balance);
// Demonstrate that jane was not inserted by checking if the returned value is 0
assert!(jane_balance == 25, "jane_balance should be 0 since she was never added");
}
जब हम उपरोक्त कोड चलाते हैं, तो पहला एश्योरेंस (assertion) विफल हो जाएगा क्योंकि कुंजी 'clark' को 50 की वैल्यू के साथ डाला गया था, इसलिए, शर्त clark_balance == 100 का मूल्यांकन गलत (false) के रूप में होता है।
यदि हम पहले एश्योरेंस को हटा (comment out) देते हैं ताकि दूसरा वाला चल सके, तो प्रोग्राम 'jane' के लिए बैलेंस प्राप्त करने के लिए आगे बढ़ेगा, जिसे डिक्शनरी में कभी नहीं डाला गया था। Cairo में, किसी ऐसी कुंजी पर .get('jane') कॉल करना जिसे स्पष्ट रूप से (explicitly) नहीं डाला गया है, वैल्यू प्रकार की डिफ़ॉल्ट वैल्यू लौटाता है, इस मामले में, 0।

डिक्शनरीज़ के अंदर कंपाउंड प्रकार (Compound types)
let mut dict: Felt252Dict<u64> = Default::default();
// ALL possible keys now have value 0 (the zero value for u64)
let value = dict.get(999); // Returns 0, even though we never inserted anything
let mut dictArray: Felt252Dict<<Array<u8>>> = Default::default();
// ALL possible keys of dictArray do not have value 0
हमने पहले उल्लेख किया है कि बनाए जाने पर डिक्शनरीज़ zero_default मेथड के माध्यम से स्वचालित रूप से सभी कुंजियों को “शून्य वैल्यू” में इनिशियलाइज़ करती हैं। हालाँकि, यह व्यवहार एरेज़ और स्ट्रक्ट्स (जैसे u256 प्रकार सहित) जैसे जटिल या समग्र (composite) प्रकारों के लिए समर्थित नहीं है। ऐसा इसलिए है क्योंकि zero_default के लिए प्रकार के पास एक शून्य वैल्यू होना आवश्यक है जिसे तब वापस किया जा सकता है जब कोई कुंजी स्पष्ट रूप से सेट नहीं की गई हो। चूंकि जटिल प्रकार आमतौर पर इस ट्रेट को लागू नहीं करते हैं, इसलिए Cairo की आवश्यकता है कि डिक्शनरीज़ में उन्हें संग्रहीत करते समय आप मैन्युअल रूप से इनिशियलाइज़ेशन और अस्तित्व की जांच (existence checks) को संभालें।
इस सीमा को हल करने के लिए, डिक्शनरीज़ में Nullable<T> पॉइंटर प्रकार का उपयोग या तो किसी वैल्यू को या उसकी अनुपस्थिति (null) को दर्शाने के लिए किया जा सकता है। डिक्शनरी हीप-आवंटित (heap-allocated) वैल्यूज़ के पॉइंटर्स को संग्रहीत करती है, और आप पढ़ते समय स्पष्ट रूप से शून्य (null) की जांच करते हैं।
नीचे दिया गया कोड दिखाता है कि Felt252Dict के अंदर एरेज़ को Nullable<Array<felt252>> में लपेटकर कैसे स्टोर किया जाए। यह हमें डिक्शनरी में फेल्ट-आधारित (felt-based) कुंजियों के साथ गतिशील डेटा (जैसे क्रमबद्ध (serialized) वैल्यूज़) को जोड़ने की अनुमति देता है।
use core::dict::Felt252Dict;
#[executable]
fn main() {
// Create an array of felt252 values
let data = array![42, 13, 88, 5];
// Initialize a dictionary that maps felt252 keys to nullable byte arrays
let mut storage: Felt252Dict<Nullable<Array<u8>>> = Default::default();
// Convert our data to bytes and store in dictionary
let byte_data = array![0x2a, 0x0d, 0x58, 0x05]; // hex representation
storage.insert(1, NullableTrait::new(byte_data));
// Store another entry
let more_data = array![0xff, 0x00, 0xaa];
storage.insert(2, NullableTrait::new(more_data));
}
यह उदाहरण दिखाता है कि कैसे एरेज़ को अद्वितीय (unique) फेल्ट कुंजियों का उपयोग करके डिक्शनरी में डाला जा सकता है, जिसमें Nullable एक सुरक्षित रैपर प्रदान करता है जो किसी वैल्यू या खाली स्थिति को दर्शा सकता है।
निष्कर्ष (Conclusion)
Cairo एक Rust-जैसी लैंग्वेज है जिसमें परिचित कंट्रोल स्ट्रक्चर्स हैं।
- न्यूमेरिक प्रकारों के लिए
felt252डेटा प्रकार डिफ़ॉल्ट है। कई डेटा प्रकार पर्दे के पीछेfelt252में परिवर्तित हो जाते हैं। - ओवरफ़्लो सुरक्षा के कारण
felt252प्रकार के बजाय साइंड और अनसाइंड (signed and unsigned) का उपयोग करना बेहतर होता है। - वैरिएबल्स डिफ़ॉल्ट रूप से अपरिवर्तनीय (immutable) होते हैं और यदि भविष्य में उनकी वैल्यू बदलेगी तो उन्हें
mutघोषित किया जाना चाहिए। - Cairo डेटा को एक साथ समूहित करने के लिए एरेज़ और डिक्शनरीज़ का समर्थन करता है।
यह लेख Cairo Programming on Starknet पर एक ट्यूटोरियल श्रृंखला का हिस्सा है।