A struktúrák egyedi adattípusok, amelyek segítségével több kapcsolódó értéket nevezhetsz el és csomagolhatsz egybe.
Egy struktúra definiálásához használd a struct kulcsszót, utána egy nevet, majd kapcsos zárójeleket, amelyekben a mezők névvel és típussal szerepelnek:
#[derive(Drop)]
struct User {
active: bool,
username: ByteArray,
email: ByteArray,
sign_in_count: u64,
}
Egy struct példányának létrehozásához add meg a struct nevét, és használj kapcsos zárójeleket a mezők kulcs-érték párjaival.
let user = User {
active: true, username: "someusername123", email: "someone@example.com", sign_in_count: 1
};
Pont jelöléssel érheted el vagy módosíthatod egy módosítható struct példány mezőit.
let mut user = User {
active: true, username: "someusername123", email: "someone@example.com", sign_in_count: 1
};
user.email = "anotheremail@example.com";
A Cairoban az értékek változtathatatlanok, és ez a változtathatatlanság biztosítja, hogy még ha sok változó ugyanarra az értékre is hivatkozik, maga az érték állandó és változatlan marad. Egy változó birtokosa az a kód, amely olvasni (és ha módosítható, írni) tudja azt a változót.
A birtoklás szabályai a Cairoban:
A scope a program azon tartománya, amelyben egy változó érvényes.
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
Amikor egy változó kikerül a scope-ból, megsemmisül, ami biztosítja, hogy a hozzá tartozó erőforrás megfelelően felszabaduljon.
Ez a Drop trait implementálásával vagy származtatásával érhető el, ami jelzi a fordítónak, hogy a típus biztonságosan megsemmisíthető, ha már nem hasznos.
#[derive(Drop)]
struct A {}
fn main() {
A {}; // this value can be destroyed after going out of scope
}
Jelenleg a Drop implementáció minden típusra származtatható, kivéve a szótárakat (Felt252Dict) és a szótárakat tartalmazó típusokat.
Egy érték áthelyezhető is, ami akkor történik, amikor az értéket átadjuk egy másik függvénynek: megsemmisül az eredeti változó, amely az értékre hivatkozott (így a változó használhatatlanná válik), és létrejön egy új változó, amely ugyanazt az értéket tárolja.
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
}
Az értékek visszaadása egyenértékű az áthelyezésükkel.
Az alábbi példa egy olyan függvényt mutat, amely visszaad egy értéket:
#[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
}
Mivel egy változó az áthelyezés után megsemmisül, a fordító megtiltja, hogy utána újra felhasználjuk.
#[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.
}
Ebben az esetben ugyanazt az értéket (a p változóban lévő Point struktúrát) próbáljuk átadni mindkét függvényhívásnak.
Miután a változót először átadtuk a foo függvénynek, a változó már nem használható.
Egyik megoldás, ha származtatjuk a Copy traitet a struktúránkon, ami megváltoztatja, mi történik, amikor egy Copy változót adunk át egy függvénynek: már nem helyezné át az értéket, hanem létrehozna egy új változót, amely ugyanarra az értékre hivatkozik.
Ez ingyenes művelet, mert a változók a Cairo absztrakciói, az értékek pedig mindig változtathatatlanok.
Ha implementálni szeretnéd a Copy traitet a típusodon, add hozzá a #[derive(Copy)] annotációt a típusdefinícióhoz.
#[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
}
A Cairónak két funkciója van arra, hogy egy értéket megsemmisítés vagy áthelyezés nélkül adjon át: a references és a snapshots.
A pillanatképek egy változtathatatlan „nézetet” nyújtanak egy értékről egy adott pillanatban, lehetővé téve, hogy megtartsd egy változó birtoklását, miközben hozzáférsz az állapotához. A változó bármilyen módosítása új memóriacellát hoz létre, a pillanatkép nézete pedig változatlan marad.
Pillanatkép létrehozásához egyszerűen tedd a változópéldány elé a @ jelet.
// 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
}
Ha egy pillanatképet vissza szeretnél alakítani normál változóvá, használhatod a desnap operátort (*), amely a @ operátor ellentéte.
Csak azok a típusok desnapelhetők, amelyek implementálják a Copy traitet.
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);
}
A hivatkozások lehetővé teszik, hogy módosítható értékeket adj át függvényeknek, és azok implicit módon visszaadódjanak, megőrizve a birtoklást a hívó kontextusában.
Ez a ref módosítóval érhető el, feltéve, hogy a változó a mut kulcsszóval módosíthatóként van deklarálva.
Frissíthetjük a korábbi Point példát, hogy módosítható hivatkozásokat használjon a Copy trait helyett, és akkor is működni fog.
#[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
}
A közeledben lévő helyi iskolának van egy nagyon aktív diákönkormányzata. A diákönkormányzatot egy elnök vezeti, és kétévente választásokat tartanak új elnök megválasztására. Az idei választáson úgy döntöttek, hogy új digitális rendszerre van szükség a szavazatok megszámlálásához.
Az iskola a segítségedet kéri az új rendszer megépítésében.
Az új szavazási rendszer egyik első eleme egy szavazatszámláló. Ez a számláló arra való, hogy nyomon kövesse, hány szavazata van egy adott jelöltnek.
Hozz létre egy new_vote_counter függvényt, amely elfogadja egy jelölt kezdeti szavazatainak számát, és visszaadja egy VoteCounter struktúra pillanatképét, a megadott kezdeti szavazatszámmal inicializálva.
let initial_votes: u32 = 2;
let counter: @VoteCounter = new_vote_counter(initial_votes);
// @VoteCounter {
// value: 2,
// }
Most már van módod új számlálók létrehozására! De most rájössz, hogy az új rendszernek is szüksége lesz egy módra, hogy lekérje a szavazatok számát egy számlálóból.
Hozz létre egy vote_count függvényt, amely argumentumként egy számlálót (@VoteCounter) fogad, és visszaadja a számlálóban lévő szavazatok számát.
let initial_votes: u32 = 3;
let vote_counter: @VoteCounter = new_vote_counter(initial_votes);
let count = vote_count(vote_counter);
// => 3
Végre eljött az idő a szavazatok megszámlálására! Most szükséged van egy módra, amellyel növelheted a szavazatok számát egy számlálóban.
Hozz létre egy increment_vote_count függvényt, amely argumentumként egy számlálót (@VoteCounter) és egy szavazatszámot fogad, majd megnöveli a számlálót ennyi szavazattal.
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
Most, hogy minden szavazatot megszámoltak, itt az idő előkészíteni az eredményhirdetést az egész iskola számára. Ehhez észreveszed, hogy nem elég, ha csak a szavazatok számlálói vannak. Kell egy mód arra, hogy a szavazatok számát egy adott jelölthöz rendeld.
Hozz létre egy new_election_result függvényt, amely megkapja egy jelölt nevét és a szavazatai számát, majd visszaad egy új választási eredményt.
let result: ElectionResult = new_election_result("Peter", 3);
// ElectionResult {
// name: "Peter",
// votes: @VoteCounter {
// value: 3,
// },
// }
A választási eredmény struktúra már létre van hozva neked, és így van definiálva:
struct ElectionResult {
name: ByteArray,
counter: @VoteCounter,
}
Itt az idő bejelenteni az iskolának az új elnököt! Az új elnököt az iskola kis digitális üzenőtábláin fogják kihirdetni.
Az üzenetnek mutatnia kell az új elnök nevét és a kapott szavazatok számát, a következő formátumban: <candidate_name> (<votes>).
Íme egy példa egy ilyen üzenetre: "Peter (51)".
Hozz létre egy display_result függvényt, amely argumentumként egy @ElectionResult-et fogad, és egy ByteArray-t ad vissza a megjelenítendő üzenettel.
let result = ElectionResult {
name: "John",
votes: @VoteCounter {
value: 32,
},
};
let msg = display_result(@result);
// => "John (32)"
Hogy megbizonyosodjanak a végeredmény pontosságáról, újraszámolták a szavazatokat. Az újraszámláláskor kiderült, hogy néhány jelölt szavazatainak száma eggyel eltért.
Hozz létre egy decrement_votes_of_candidate függvényt, amely megkapja a végeredményeket és egy jelölt nevét, akinek csökkentened kell a szavazatai számát.
A végeredmények egy Felt252Dict<u32> formájában vannak megadva, ahol a kulcsok a jelöltek nevei, az értékek pedig az adott jelölt összes szavazata.
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
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Cairo nyelvet 25 fogalom68 feladat segítségével, valódi emberi mentorálással, mindez ingyen.