स्ट्रक्ट कस्टम डेटा टाइप होते हैं, जो आपको कई आपस में जुड़ी वैल्यू को एक नाम देनकर एक साथ पैक करने देते हैं।
किसी स्ट्रक्ट को बनाने के लिए struct कीवर्ड के बाद नाम लिखिए, और फिर कर्ली ब्रैकेट के अंदर फील्ड को उनके नाम और टाइप के साथ लिखिए:
#[derive(Drop)]
struct User {
active: bool,
username: ByteArray,
email: ByteArray,
sign_in_count: u64,
}
किसी struct का इंस्टेंस बनाने के लिए struct का नाम लिखिए, और फील्ड के लिए की-वैल्यू जोड़ों वाले कर्ली ब्रैकेट इस्तेमाल कीजिए।
let user = User {
active: true, username: "someusername123", email: "someone@example.com", sign_in_count: 1
};
किसी म्यूटेबल struct इंस्टेंस के फील्ड पढ़ने या बदलने के लिए डॉट नोटेशन इस्तेमाल कीजिए।
let mut user = User {
active: true, username: "someusername123", email: "someone@example.com", sign_in_count: 1
};
user.email = "anotheremail@example.com";
Cairo में वैल्यू इम्यूटेबल होती हैं, और यह इम्यूटेबिलिटी यह पक्का करती है कि भले ही कई वेरिएबल एक ही वैल्यू की ओर इशारा करें, वैल्यू खुद हमेशा वैसी ही बनी रहती है और बदलती नहीं। किसी वेरिएबल का ओनर वह कोड होता है जो उस वेरिएबल को पढ़ सकता है (और म्यूटेबल होने पर लिख भी सकता है)।
Cairo में ओनरशिप के नियम:
स्कोप प्रोग्राम के अंदर वह दायरा है जिसमें कोई वेरिएबल मान्य रहता है।
fn main() {
// `x` is not valid here, it has not been declared yet
let x: u32 = 3; // `x` is declared, it is valid from this point forward
// do stuff with `x`
} // this scope ended here, `x` goes out of scope and is no longer valid
जब कोई वेरिएबल स्कोप से बाहर जाता है तो वह नष्ट हो जाता है, जिससे यह पक्का होता है कि उससे जुड़ा रिसोर्स ठीक से मुक्त हो जाता है।
यह Drop ट्रेट को इम्प्लीमेंट या डिराइव करके पूरा किया जाता है, जो कंपाइलर को बताता है कि जब टाइप का इस्तेमाल खत्म हो जाए तो उसे सुरक्षित रूप से नष्ट किया जा सकता है।
#[derive(Drop)]
struct A {}
fn main() {
A {}; // this value can be destroyed after going out of scope
}
फिलहाल Drop का इम्प्लीमेंटेशन सभी टाइप के लिए डिराइव किया जा सकता है, सिवाय डिक्शनरी (Felt252Dict) और डिक्शनरी रखने वाले टाइप के।
कोई वैल्यू मूव भी की जा सकती है। ऐसा तब होता है जब वैल्यू किसी दूसरे फंक्शन को दी जाती है: उस वैल्यू की ओर इशारा करने वाला मूल वेरिएबल नष्ट हो जाता है (और इस तरह बेकार हो जाता है), और उसी वैल्यू को रखने के लिए एक नया वेरिएबल बन जाता है।
fn foo(num: u32) {
// do something with p
}
fn main() {
let num: u32 = 10;
foo(num);
// ownership of `num` was moved into `foo` and is thus no longer valid in this scope
}
वैल्यू लौटाना भी उन्हें मूव करने के बराबर ही है।
नीचे एक ऐसे फंक्शन का उदाहरण है जो कोई वैल्यू लौटाता है:
#[derive(Drop)]
struct A {}
fn main() {
// main now owns a1
let a1 = gives_ownership();
// a2 comes into scope
let a2 = A {};
// a2 is moved into takes_and_gives_back, which also moves its return value into a3
let a3 = takes_and_gives_back(a2);
} // Here, a3 and a1 go out of scope and are dropped. a2 was moved, so nothing happens.
// Moves its return value into the function that calls it
fn gives_ownership() -> A {
let some_a = A {};
some_a
}
// This function takes an instance some_a of A and returns it
fn takes_and_gives_back(some_a: A) -> A {
some_a
}
चूँकि कोई वेरिएबल मूव होने के बाद नष्ट हो जाता है, कंपाइलर हमें उसे उसके बाद दोबारा इस्तेमाल करने की इजाज़त नहीं देता।
#[derive(Drop)]
struct Point {
x: u128,
y: u128,
}
fn foo(p: Point) {
// do something with p
}
fn main() {
let p = Point { x: 5, y: 10 };
foo(p);
foo(p); // compiler error: Variable was previously moved.
}
इस उदाहरण में हम एक ही वैल्यू (अर्थात p वेरिएबल में रखा Point स्ट्रक्ट) दोनों फंक्शन कॉल को देने की कोशिश कर रहे हैं।
पहली बार foo फंक्शन को वेरिएबल देने के बाद वह वेरिएबल अब इस्तेमाल करने लायक नहीं रहता।
इसका एक तरीका यह है कि हम अपने स्ट्रक्ट पर Copy ट्रेट डिराइव कर लें। इससे यह बदल जाएगा कि जब हम कोई Copy वेरिएबल किसी फंक्शन को देते हैं तो क्या होता है: अब वैल्यू मूव नहीं होगी, बल्कि उसी वैल्यू की ओर इशारा करने वाला एक नया वेरिएबल बनेगा।
यह ऑपरेशन मुफ्त होता है, क्योंकि वेरिएबल Cairo का एक एब्स्ट्रैक्शन हैं और वैल्यू हमेशा इम्यूटेबल रहती हैं।
अपने टाइप पर Copy ट्रेट इम्प्लीमेंट करने के लिए टाइप की परिभाषा में #[derive(Copy)] एनोटेशन जोड़िए।
#[derive(Copy, Drop)]
struct Point {
x: u128,
y: u128,
}
fn foo(p: Point) {
// do something with p
}
fn main() {
let p = Point { x: 5, y: 10 };
foo(p);
foo(p); // this now works
}
Cairo में किसी वैल्यू को नष्ट या मूव किए बिना देने के लिए दो सुविधाएँ हैं, जिन्हें references और snapshots कहते हैं।
स्नैपशॉट किसी खास समय पर वैल्यू का एक इम्यूटेबल "दृश्य" देते हैं, जिससे आप वेरिएबल का ओनरशिप बनाए रखते हुए उसकी स्थिति पढ़ सकते हैं। वेरिएबल में कोई भी बदलाव एक नई मेमोरी सेल बनाता है, और स्नैपशॉट का दृश्य वैसा ही बना रहता है।
स्नैपशॉट बनाने के लिए वेरिएबल के आगे @ लगा दीजिए।
// this function takes in a snapshot using `@` with the data-type
fn get_len(arr: @Array<u128>) -> u32 { //
arr.len()
}
fn foo(arr: Array<u128>) {
arr.pop_front();
}
fn main() {
let mut new_arr: Array<u128> = array![1, 2, 3, 4, 5, 6, 7, 8];
let snapshot = @new_arr; // using `@` to create a snapshot of a variable
let len = get_len(snapshot); // pass the snapshot as argument, retaining ownership
foo(new_arr); // `new_arr` is still valid up until here and no further
}
किसी स्नैपशॉट को वापस सामान्य वेरिएबल में बदलने के लिए आप डीस्नैप ऑपरेटर * इस्तेमाल कर सकते हैं, जो @ ऑपरेटर का उल्टा काम करता है।
सिर्फ वही टाइप डीस्नैप किए जा सकते हैं जो Copy ट्रेट इम्प्लीमेंट करते हैं।
fn square(n: @u32) -> u32 {
let n = *n; // we desnap the value
n * n
}
fn main() {
let num: u32 = 10;
let squared = square(@num); // we use `@` to create a snapshot
println!("{} squared = {}", num, squared);
}
रेफरेंस आपको फंक्शन को म्यूटेबल वैल्यू देने देते हैं और वे वैल्यू आपको अपने आप वापस मिल जाती हैं, जिससे ओनरशिप कॉल करने वाले कॉन्टेक्स्ट में ही बनी रहती है।
यह ref मॉडिफायर से किया जा सकता है, बशर्ते वेरिएबल को mut कीवर्ड से म्यूटेबल घोषित किया गया हो।
हम पिछले Point उदाहरण को बदलकर Copy ट्रेट पर निर्भर रहने की जगह म्यूटेबल रेफरेंस इस्तेमाल कर सकते हैं, और यह तब भी काम करेगा।
#[derive(Drop)]
struct Point {
x: u128,
y: u128,
}
fn foo(ref p: Point) {
// we can now update the passed values and the changes
// will be visible in the calling context
p.x += 1;
}
fn main() {
let mut p = Point { x: 5, y: 10 };
foo(ref p);
foo(ref p); // this now works even without the Copy trait
println!("p.x = {}", p.x); // p.x = 7
}
आपके आस-पास के एक स्थानीय स्कूल में छात्रों की एक बहुत सक्रिय संस्था है। इस छात्र संस्था का संचालन एक अध्यक्ष करता है, और हर 2 साल में एक बार नया अध्यक्ष चुनने के लिए चुनाव होते हैं। इस साल के चुनाव में तय हुआ कि वोट गिनने के लिए एक नई डिजिटल प्रणाली की ज़रूरत है।
यह नई प्रणाली बनाने में स्कूल को आपकी मदद चाहिए।
नई वोटिंग प्रणाली के लिए सबसे पहले जिन चीज़ों की ज़रूरत पड़ेगी, उनमें से एक है वोट काउंटर। इस काउंटर की मदद से किसी एक उम्मीदवार को मिले वोटों का हिसाब रखा जा सकता है।
एक फंक्शन new_vote_counter बनाइए जो किसी उम्मीदवार के शुरुआती वोटों की संख्या लेता है और VoteCounter स्ट्रक्चर का एक स्नैपशॉट लौटाता है, जिसे दिए गए शुरुआती वोटों की संख्या के साथ शुरू किया गया हो।
let initial_votes: u32 = 2;
let counter: @VoteCounter = new_vote_counter(initial_votes);
// @VoteCounter {
// value: 2,
// }
अब आपके पास नए काउंटर बनाने का तरीका है! लेकिन अब आप देखते हैं कि नई प्रणाली को किसी काउंटर से वोटों की संख्या निकालने का तरीका भी चाहिए।
एक फंक्शन vote_count बनाइए जो एक काउंटर (@VoteCounter) को आर्गुमेंट के रूप में लेगा और उस काउंटर में मौजूद वोटों की संख्या लौटाएगा।
let initial_votes: u32 = 3;
let vote_counter: @VoteCounter = new_vote_counter(initial_votes);
let count = vote_count(vote_counter);
// => 3
अब वोट गिनने का समय आ ही गया है! अब आपको काउंटर में वोट बढ़ाने का तरीका चाहिए।
एक फंक्शन increment_vote_count बनाइए जो एक काउंटर (@VoteCounter) और वोटों की एक संख्या आर्गुमेंट के रूप में लेगा, और उस काउंटर को उतने वोटों से बढ़ा देगा।
let votes: u32 = 3;
let vote_counter: @VoteCounter = new_vote_counter(initial_votes);
let vote_counter: VoteCounter = increment_vote_count(vote_counter, 2);
let count = vote_count(@vote_counter);
// => 5
अब जब सारे वोट गिन लिए गए हैं, तो पूरे स्कूल को परिणाम सुनाने की तैयारी का समय आ गया है। इसके लिए आप देखते हैं कि सिर्फ वोटों के काउंटर होना काफी नहीं है। वोटों की संख्या को किसी खास उम्मीदवार से जोड़ने का तरीका भी होना चाहिए।
एक फंक्शन new_election_result बनाइए जो किसी उम्मीदवार का नाम और उसके वोटों की संख्या लेता है और एक नया चुनाव परिणाम लौटाता है।
let result: ElectionResult = new_election_result("Peter", 3);
// ElectionResult {
// name: "Peter",
// votes: @VoteCounter {
// value: 3,
// },
// }
चुनाव परिणाम का स्ट्रक्चर पहले से आपके लिए बनाया गया है और इसे इस तरह परिभाषित किया गया है:
struct ElectionResult {
name: ByteArray,
counter: @VoteCounter,
}
अब स्कूल को नए अध्यक्ष की घोषणा करने का समय आ गया है! नए अध्यक्ष की घोषणा स्कूल के छोटे डिजिटल मैसेज बोर्डों पर की जाएगी।
मैसेज में नए अध्यक्ष का नाम और उन्हें मिले वोट इस प्रारूप में दिखने चाहिए: <candidate_name> (<votes>).
ऐसे मैसेज का एक उदाहरण यह है: "Peter (51)".
एक फंक्शन display_result बनाइए जो एक @ElectionResult को आर्गुमेंट के रूप में लेगा और दिखाने वाला मैसेज एक ByteArray के रूप में लौटाएगा।
let result = ElectionResult {
name: "John",
votes: @VoteCounter {
value: 32,
},
};
let msg = display_result(@result);
// => "John (32)"
अंतिम परिणाम सही हैं या नहीं, यह पक्का करने के लिए वोटों की दोबारा गिनती की गई। दोबारा गिनती करने पर पता चला कि कुछ उम्मीदवारों के वोटों की संख्या में एक का अंतर था।
एक फंक्शन decrement_votes_of_candidate बनाइए जो अंतिम परिणाम और उस उम्मीदवार का नाम लेता है, जिसके वोटों की संख्या घटानी है।
अंतिम परिणाम Felt252Dict<u32> के रूप में दिए गए हैं, जिसमें कीज़ उम्मीदवारों के नाम हैं और वैल्यूज़ उनके कुल वोट हैं।
let mut final_results: Felt252Dict<u32> = Default::default();
final_results.insert('Mary', 10);
final_results.insert('John', 51);
decrement_votes_of_candidate(ref final_results, 'Mary');
let decremented_votes = final_results.get('Mary');
// => 9
Exercism पर साइन अप कीजिए और Cairo को 25 कॉन्सेप्ट68 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।