Cél és előfeltétel

Egy változó átadása első látásra minden típusnál ugyanolyan műveletnek tűnhet. Rustban azonban fontos, hogy a típus egyszerűen másolható-e, vagy egy saját erőforrással rendelkező érték tulajdonjoga kerül át. Ebben a leckében a fordító szemszögéből követed az adat útját. A cél, hogy ne véletlenszerű másolásokkal tüntesd el a hibaüzeneteket, hanem el tudd mondani, ki használhatja az értéket egy művelet után.

Előfeltétel a függvények és visszatérés, valamint az egyszerű változók ismerete. A példákat külön edition 2024 főprogramokként futtasd. A megoldások nem használnak külső könyvtárat, és minden állítás a memóriában létrehozott adatokra vonatkozik. Minden kötés mellé rajzold fel, melyik értékhez férhet hozzá a következő sorban.

Egész számoknál másolat készül

Egy kis egész szám átadása után az eredeti kötés rendszerint tovább használható, mert az egész típusok megvalósítják a Copy tulajdonságot. A másolat önálló érték: az új változó növelése nem módosítja a régi számot. Nem két név között működő rejtett szinkronizálás történik, és nem a név vagy a mut jelölés dönti el a másolhatóságot.

A Copy nem minden típus automatikus képessége. Egy saját szövegpuffert kezelő String nem másolódik így. Ha az értékadást úgy képzelnéd el, hogy ugyanannak a puffernek két független gazdája lesz, akkor a felszabadítás felelőssége összezavarodna. A move ezt a tulajdont adja tovább, és a régi kötésen keresztül az érték ezután nem használható.

A move nem karakterenkénti másolás

A move fogalma a program használati szabályát írja le: az érték felelőse megváltozik. Nem ígér teljes szövegmásolatot, és nem azt jelenti, hogy a betűk egyik változónévből átvándorolnak egy másik név szövegébe. A változónevek a forráskód részei, a String tartalma ettől külön adat. Az átadás után a tulajdont fogadó fél olvashatja és módosíthatja a szöveget.

Ha az eredeti kötést egy String átadása után újra használni próbálod, a fordító elutasítja a kódot. Ez nem futás közbeni, időnként felbukkanó hiba. A forrás használati szabálya már fordításkor sérül. A javítás attól függ, mire van szükség: tulajdonátadásra, ideiglenes hozzáférésre vagy valóban külön másolatra. A következő leckében az ideiglenes hozzáférést is megismered.

A függvényhatár is adatátadás

A függvényparaméterhez átadott String ugyanúgy tulajdont válthat, mint egy új kötés létrehozásakor. Egy függvény azonban vissza is adhatja az értéket. Ilyenkor a hívó új kötésben veheti át a visszatérő tulajdont. A paraméterben és a visszatérésnél látható String típus ezért nem mellékes részlet: a függvény használati szerződésének része.

A clone külön, önálló String értéket készít. Ez olyankor indokolt, ha két külön módosítható szöveget akarsz megtartani. Több munkát és memóriát igényelhet, mint egy kölcsönzött hozzáférés, ezért ne minden fordítási hibára ezt válaszd reflexből. Az állításokkal azt is megvizsgáljuk, hogy az egyik másolat bővítése valóban nem változtatja meg a másikat.

1. feladat: két önálló szám

Hozz létre egy tizenkét darabot tároló egész számot, majd rendeld egy második kötéshez. A második értéket növeld hárommal. Az eredeti maradjon tizenkettő, a másik legyen tizenöt. Ellenőrizd mindkettőt, majd azt is, hogy a növelés utáni összegük huszonhét. A feladat nem referenciákat, hanem két egész értéket használ.

Megoldás
fn main() {
    let eredeti: u32 = 12;
    let mut masolat = eredeti;
    masolat += 3;
    assert_eq!(eredeti, 12);
    assert_eq!(masolat, 15);
    assert_eq!(eredeti + masolat, 27);
}

A második kötés módosíthatósága nem teszi módosíthatóvá az elsőt. A két szám azonossága csak a másolás pillanatában állt fenn; később önállóan használjuk őket.

2. feladat: szöveg tulajdonának továbbadása

Írj függvényt, amely átvesz egy String értéket, felkiáltójelet fűz hozzá, majd visszaadja. A hívó az eredményt új kötésbe vegye át. Ellenőrizd a szokásos név mellett az üres szöveget is. A régi kötés későbbi használata helyett a visszaadott értéket vizsgáld, mert a függvényhívás tulajdont vett át.

Megoldás
fn hangsulyoz(mut szoveg: String) -> String {
    szoveg.push('!');
    szoveg
}
fn main() {
    let nev = String::from("Eszter");
    let hangsulyos = hangsulyoz(nev);
    assert_eq!(hangsulyos, "Eszter!");
    assert_eq!(hangsulyoz(String::new()), "!");
}

Az üres bemenethez is ugyanaz a művelet tartozik. A módosítás után a függvény nem csak hosszt vagy jelzést ad vissza, hanem magát a teljes, immár módosított String értéket.

3. feladat: tudatosan különálló szövegek

Egy alapcímkéből készíts önálló másolatot. Csak a másolat végére fűzd a „ másolat” szöveget, az eredeti maradjon változatlan. Vizsgáld meg mindkét teljes tartalmat. Ez a feladat valóban két eltérő szöveget kíván, ezért a klónozásnak világos célja van, nem pusztán a fordító üzenetének elhallgattatása.

Megoldás
fn main() {
    let alap = String::from("Napló");
    let mut kulon = alap.clone();
    kulon.push_str(" másolat");
    assert_eq!(alap, "Napló");
    assert_eq!(kulon, "Napló másolat");
    let ures = String::new();
    let ures_masolat = ures.clone();
    assert!(ures.is_empty() && ures_masolat.is_empty());
}

A tartalmi ellenőrzés az önálló változtatás eredményét vizsgálja. Nem memóriacímekből találgatjuk, hogyan dolgozott a fordító vagy milyen optimalizálást végzett.

Elfogadási feltételek

  • Az egész másolat növelése mellett az eredeti érték tizenkettő marad.
  • A tulajdont átvevő függvény visszaadja a módosított String értéket, az üres eset eredménye egyetlen felkiáltójel.
  • A klónozott címke bővítése nem módosítja az eredeti szöveget; ezt teljes tartalmak összehasonlítása igazolja.

Továbblépés

Már követni tudod az értékek másolását és tulajdonváltását. A megosztott és kizárólagos kölcsönzés leckéje megmutatja, hogyan adhatsz hozzáférést a tulajdon végleges átadása nélkül.